Invite to group

This commit is contained in:
Natsumi
2022-12-09 21:47:40 +13:00
parent f39eef07c4
commit 3a062bf4b4
4 changed files with 253 additions and 74 deletions

View File

@@ -22,6 +22,7 @@ using System.Net.Sockets;
using System.Text;
using System.Collections.Generic;
using System.Threading;
using System.IO.Pipes;
namespace VRCX
{
@@ -320,6 +321,16 @@ namespace VRCX
return false;
}
public void IPCAnnounceStart()
{
var ipcClient = new NamedPipeClientStream(".", "vrcx-ipc", PipeDirection.InOut);
ipcClient.Connect();
if (!ipcClient.IsConnected)
return;
var buffer = Encoding.UTF8.GetBytes($"{{\"type\":\"VRCXLaunch\"}}" + (char)0x00);
ipcClient.BeginWrite(buffer, 0, buffer.Length, IPCClient.OnSend, ipcClient);
}
public void ExecuteAppFunction(string function, string json)
{
if (MainForm.Instance != null)

View File

@@ -2565,7 +2565,7 @@ speechSynthesis.getVoices();
if (
ref.type === 'friendRequest' ||
ref.type === 'hiddenFriendRequest' ||
ref.type === 'group.invite'
ref.type.includes('.')
) {
for (var i = array.length - 1; i >= 0; i--) {
if (array[i].id === ref.id) {
@@ -8017,6 +8017,7 @@ speechSynthesis.getVoices();
$app.migrateMemos();
$app.migrateFriendLog(args.json.id);
}
await AppApi.IPCAnnounceStart();
});
$app.methods.loadPlayerList = function () {
@@ -8941,7 +8942,6 @@ speechSynthesis.getVoices();
$app.data.photonLobbyWatcherLoop = false;
$app.data.photonLobbyTimeout = [];
$app.data.photonLobbyJointime = new Map();
$app.data.photonLobbyBots = [];
$app.data.photonEvent7List = new Map();
$app.data.photonLastEvent7List = '';
$app.data.photonLastChatBoxMsg = new Map();
@@ -9053,7 +9053,6 @@ speechSynthesis.getVoices();
$app.methods.photonLobbyWatcherLoopStop = function () {
this.photonLobbyWatcherLoop = false;
this.photonLobbyTimeout = [];
this.photonLobbyBots = [];
AppApi.ExecuteVrOverlayFunction('updateHudTimeout', '[]');
};
@@ -9140,56 +9139,9 @@ speechSynthesis.getVoices();
this.photonLobbyTimeout = hudTimeout;
this.getCurrentInstanceUserList();
}
this.photonBotCheck(dtNow);
workerTimers.setTimeout(() => this.photonLobbyWatcher(), 500);
};
$app.methods.photonBotCheck = function (dtNow) {
if (this.photonLobbyCurrentUser === 0) {
return;
}
var photonBots = [];
this.photonLobbyCurrent.forEach((ref, id) => {
if (this.photonLobbyJointime.has(id)) {
var {joinTime, hasInstantiated, avatarEyeHeight} =
this.photonLobbyJointime.get(id);
}
var text = '';
if (avatarEyeHeight < 0) {
text = 'Photon bot has joined, invalid avatarEyeHeight';
} else if (
joinTime &&
joinTime + 11000 < dtNow &&
!hasInstantiated
) {
text = 'User failed to instantiate after 10 seconds';
}
if (text && id !== this.photonLobbyCurrentUser) {
if (!this.photonLobbyBots.includes(id)) {
this.addEntryPhotonEvent({
photonId: id,
text,
type: 'PhotonBot',
color: 'yellow',
created_at: new Date().toJSON()
});
var entry = {
created_at: new Date().toJSON(),
type: 'Event',
data: `${text} ${this.getDisplayNameFromPhotonId(
id
)} (${this.getUserIdFromPhotonId(id)})`
};
this.queueGameLogNoty(entry);
this.addGameLog(entry);
database.addGamelogEventToDatabase(entry);
}
photonBots.unshift(id);
}
});
this.photonLobbyBots = photonBots;
};
$app.data.photonEventTableFilter = '';
$app.data.photonEventTableTypeFilter = [];
$app.data.photonEventTableTypeOverlayFilter = [];
@@ -9922,7 +9874,6 @@ speechSynthesis.getVoices();
}
if (data.avatarEyeHeight < 0) {
text = 'Photon bot has joined, invalid avatarEyeHeight';
this.photonLobbyBots.unshift(photonId);
} else if (data.user.last_platform === 'android' && !data.inVRMode) {
var text = 'User joined as Quest in desktop mode';
} else if (
@@ -12057,7 +12008,7 @@ speechSynthesis.getVoices();
if (
ref.type !== 'friendRequest' &&
ref.type !== 'hiddenFriendRequest' &&
ref.type !== 'group.invite'
!ref.type.includes('.')
) {
database.addNotificationToDatabase(ref);
}
@@ -15078,6 +15029,8 @@ speechSynthesis.getVoices();
this.showGalleryDialog();
} else if (command === 'Copy User') {
this.copyUser(D.id);
} else if (command === 'Invite To Group') {
this.showInviteGroupDialog('', D.id);
} else {
this.$confirm(`Continue? ${command}`, 'Confirm', {
confirmButtonText: 'Confirm',
@@ -20483,6 +20436,9 @@ speechSynthesis.getVoices();
AppApi.FocusWindow();
this.eventLaunchCommand(data.command);
break;
case 'VRCXLaunch':
console.log('VRCXLaunch:', data);
break;
default:
console.log('IPC:', data);
}
@@ -21203,7 +21159,7 @@ speechSynthesis.getVoices();
$app.data.databaseVersion = configRepository.getInt('VRCX_databaseVersion');
$app.methods.updateDatabaseVersion = async function () {
var databaseVersion = 1;
var databaseVersion = 2;
if (this.databaseVersion !== databaseVersion) {
console.log(
`Updating database from ${this.databaseVersion} to ${databaseVersion}...`
@@ -21212,6 +21168,7 @@ speechSynthesis.getVoices();
await database.fixGameLogTraveling(); // fix bug with gameLog location being set as traveling
await database.fixNegativeGPS(); // fix GPS being a negative value due to VRCX bug with traveling
await database.fixBrokenLeaveEntries(); // fix user instance timer being higher than current user location timer
await database.fixBrokenGroupInvites(); // fix notification v2 in wrong table
this.databaseVersion = databaseVersion;
configRepository.setInt('VRCX_databaseVersion', databaseVersion);
console.log('Database update complete.');
@@ -22590,6 +22547,7 @@ speechSynthesis.getVoices();
});
if ($app.groupDialog.visible && $app.groupDialog.id === groupId) {
$app.groupDialog.inGroup = json.membershipStatus === 'member';
this.getGroup({groupId});
}
});
@@ -22615,6 +22573,14 @@ speechSynthesis.getVoices();
var groupId = args.params.groupId;
if ($app.groupDialog.visible && $app.groupDialog.id === groupId) {
$app.groupDialog.inGroup = false;
this.getGroup({groupId});
}
if (
$app.userDialog.visible &&
$app.userDialog.id === this.currentUser.id &&
$app.userDialog.representedGroup.id === groupId
) {
$app.getCurrentUserRepresentedGroup();
}
});
@@ -22672,12 +22638,7 @@ speechSynthesis.getVoices();
$app.userDialog.visible &&
$app.userDialog.id === this.currentUser.id
) {
this.getRepresentedGroup({userId: this.currentUser.id}).then(
(args1) => {
$app.userDialog.representedGroup = args1.json;
return args1;
}
);
$app.getCurrentUserRepresentedGroup();
}
});
@@ -22749,12 +22710,7 @@ speechSynthesis.getVoices();
$app.userDialog.visible &&
$app.userDialog.id === this.currentUser.id
) {
this.getRepresentedGroup({userId: this.currentUser.id}).then(
(args1) => {
$app.userDialog.representedGroup = args1.json;
return args1;
}
);
$app.getCurrentUserRepresentedGroup();
}
this.$emit('GROUP', {
json,
@@ -22838,6 +22794,28 @@ speechSynthesis.getVoices();
args.ref = this.applyGroupMember(args.json);
});
/*
params: {
groupId: string,
userId: string
}
*/
API.sendGroupInvite = function (params) {
return this.call(`groups/${params.groupId}/invites`, {
method: 'POST',
params: {
userId: params.userId
}
}).then((json) => {
var args = {
json,
params
};
this.$emit('GROUP:INVITE', args);
return args;
});
};
/*
params: {
groupId: string
@@ -23101,6 +23079,9 @@ speechSynthesis.getVoices();
case 'Unsubscribe To Announcements':
this.setGroupSubscription(D.id, false);
break;
case 'Invite To Group':
this.showInviteGroupDialog(D.id, '');
break;
}
};
@@ -23147,6 +23128,10 @@ speechSynthesis.getVoices();
});
};
API.$on('LOGOUT', function () {
$app.groupDialog.visible = false;
});
$app.methods.leaveGroup = function (groupId) {
return API.leaveGroup({
groupId
@@ -23286,6 +23271,128 @@ speechSynthesis.getVoices();
return false;
};
$app.methods.getCurrentUserRepresentedGroup = function () {
return API.getRepresentedGroup({
userId: API.currentUser.id
}).then((args) => {
this.userDialog.representedGroup = args.json;
return args;
});
};
// group invite users
$app.data.inviteGroupDialog = {
visible: false,
loading: false,
groupId: '',
groupName: '',
userId: '',
userIds: [],
userObject: {},
groups: []
};
$app.methods.showInviteGroupDialog = function (groupId, userId) {
this.$nextTick(() => adjustDialogZ(this.$refs.inviteGroupDialog.$el));
var D = this.inviteGroupDialog;
D.userIds = [];
D.groups = [];
D.groupId = groupId;
D.groupName = groupId;
D.userId = userId;
D.userObject = {};
D.visible = true;
D.loading = true;
if (groupId) {
API.getCachedGroup({
groupId
})
.then((args) => {
D.groupName = args.ref.name;
})
.catch(() => {
D.groupId = '';
});
this.isAllowedToInviteToGroup();
}
API.getGroups({n: 100, userId: API.currentUser.id}).then((args) => {
this.inviteGroupDialog.groups = args.json;
D.loading = false;
});
if (userId) {
API.getCachedUser({userId}).then((args) => {
D.userObject = args.ref;
});
D.userIds = [userId];
}
};
API.$on('LOGOUT', function () {
$app.inviteGroupDialog.visible = false;
});
$app.methods.sendGroupInvite = function () {
this.$confirm('Continue? Invite To Group', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info',
callback: (action) => {
var D = this.inviteGroupDialog;
if (action !== 'confirm' || D.loading === true) {
return;
}
D.loading = true;
var inviteLoop = () => {
if (D.userIds.length > 0) {
var receiverUserId = D.userIds.shift();
API.sendGroupInvite({
groupId: D.groupId,
userId: receiverUserId
}).finally(inviteLoop);
} else {
D.loading = false;
}
};
inviteLoop();
}
});
};
$app.methods.isAllowedToInviteToGroup = function () {
var D = this.inviteGroupDialog;
var groupId = D.groupId;
if (!groupId) {
return;
}
D.loading = true;
API.getGroup({groupId})
.then((args) => {
var group = args.ref;
if (group.joinState === 'open') {
return args;
}
if (
group.myMember &&
group.myMember.permissions &&
group.myMember.permissions.includes('group-invites-manage')
) {
return args;
}
// not allowed to invite
D.groupId = '';
this.$message({
type: 'error',
message: 'You are not allowed to invite to this group'
});
return args;
})
.finally(() => {
D.loading = false;
});
};
$app = new Vue($app);
window.$app = $app;
})();

View File

@@ -807,11 +807,10 @@ html
el-button(type="text" icon="el-icon-close" size="mini" @click="sendNotificationResponse(scope.row.id, scope.row.link, 'decline')")
el-tooltip(placement="top" content="Block Group" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-circle-close" size="mini" @click="sendNotificationResponse(scope.row.id, scope.row.link, 'block')")
template(v-if="scope.row.type !== 'requestInviteResponse' && scope.row.type !== 'inviteResponse' && scope.row.type !== 'message' && scope.row.type !== 'group.invite'")
template(v-if="scope.row.type !== 'requestInviteResponse' && scope.row.type !== 'inviteResponse' && scope.row.type !== 'message' && !scope.row.type.includes('group.') && !scope.row.type.includes('moderation.')")
el-tooltip(placement="top" content="Decline" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-close" size="mini" style="margin-left:5px" @click="hideNotification(scope.row)")
template(v-if="scope.row.type !== 'friendRequest' && scope.row.type !== 'hiddenFriendRequest' && scope.row.type !== 'group.invite'")
template(v-if="scope.row.type !== 'friendRequest' && scope.row.type !== 'hiddenFriendRequest' && !scope.row.type.includes('group.') && !scope.row.type.includes('moderation.')")
el-tooltip(placement="top" content="Delete log" :disabled="hideTooltips")
el-button(type="text" icon="el-icon-delete" size="mini" style="margin-left:5px" @click="deleteNotificationLog(scope.row)")
@@ -1633,6 +1632,7 @@ html
template(v-if="lastLocation.location && isGameRunning && checkCanInvite(lastLocation.location)")
el-dropdown-item(icon="el-icon-message" command="Invite") Invite
el-dropdown-item(icon="el-icon-message" command="Invite Message") Invite With Message
el-dropdown-item(icon="el-icon-message" command="Invite To Group") Invite To Group
template(v-else-if="userDialog.incomingRequest")
el-dropdown-item(icon="el-icon-check" command="Accept Friend Request") Accept Friend Request
el-dropdown-item(icon="el-icon-close" command="Decline Friend Request") Decline Friend Request
@@ -2222,7 +2222,7 @@ html
template(v-if="groupDialog.ref.myMember")
el-dropdown-item(v-if="groupDialog.ref.myMember.isSubscribedToAnnouncements" icon="el-icon-close" command="Unsubscribe To Announcements" divided) Unsubscribe To Announcements
el-dropdown-item(v-else icon="el-icon-check" command="Subscribe To Announcements" divided) Subscribe To Announcements
el-dropdown-item(icon="el-icon-message" command="Invite To Group") Invite To Group
template(v-if="groupDialog.ref.myMember && groupDialog.ref.privacy === 'default'")
el-dropdown-item(icon="el-icon-view" command="Visibility Everyone" divided) #[i.el-icon-check(v-if="groupDialog.ref.myMember.visibility === 'visible'")] Visibility Everyone
el-dropdown-item(icon="el-icon-view" command="Visibility Friends") #[i.el-icon-check(v-if="groupDialog.ref.myMember.visibility === 'friends'")] Visibility Friends
@@ -2476,15 +2476,15 @@ html
img(v-lazy="userImage(friend.ref)")
.detail
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
span(v-else v-text="friend.id")
el-option-group(v-if="friendsGroup2.length" label="OFFLINE")
span(v-else v-text="friend.id")
el-option-group(v-if="friendsGroup3.length" label="OFFLINE")
el-option.x-friend-item(v-for="friend in friendsGroup3" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
template(v-if="friend.ref")
.avatar
img(v-lazy="userImage(friend.ref)")
.detail
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
span(v-else v-text="friend.id")
span(v-else v-text="friend.id")
el-form-item(label="Group ID" v-if="newInstanceDialog.accessType === 'group'")
el-input(v-model="newInstanceDialog.groupId" placeholder="grp_UUID" size="mini")
el-form-item(label="Location")
@@ -3228,7 +3228,7 @@ html
el-button(type="default" size="small" @click="displayGalleryUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") Upload
el-button(type="default" size="small" @click="setProfilePicOverride('')" icon="el-icon-close" :disabled="!API.currentUser.profilePicOverride") Clear
br
.x-friend-item(v-for="image in galleryTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default")
.x-friend-item(v-if="image.versions && image.versions.length > 0" v-for="image in galleryTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default")
.vrcplus-icon(v-if="image.versions[image.versions.length - 1].file.url" @click="setProfilePicOverride(image.id)" :class="{ 'current-vrcplus-icon': compareCurrentProfilePic(image.id) }")
img.avatar(v-lazy="image.versions[image.versions.length - 1].file.url")
div(style="float:right;margin-top:5px")
@@ -3243,7 +3243,7 @@ html
el-button(type="default" size="small" @click="displayVRCPlusIconUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") Upload
el-button(type="default" size="small" @click="setVRCPlusIcon('')" icon="el-icon-close" :disabled="!API.currentUser.userIcon") Clear
br
.x-friend-item(v-for="image in VRCPlusIconsTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default")
.x-friend-item(v-if="image.versions && image.versions.length > 0" v-for="image in VRCPlusIconsTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default")
.vrcplus-icon(v-if="image.versions[image.versions.length - 1].file.url" @click="setVRCPlusIcon(image.id)" :class="{ 'current-vrcplus-icon': compareCurrentVRCPlusIcon(image.id) }")
img.avatar(v-lazy="image.versions[image.versions.length - 1].file.url")
div(style="float:right;margin-top:5px")
@@ -3564,6 +3564,61 @@ html
el-tag(v-for="user in chatboxUserBlacklist" type="info" disable-transitions="true" :key="user[0]" style="margin-right:5px;margin-top:5px" closable @close="deleteChatboxUserBlacklist(user[0])")
span {{user[1]}}
//- dialog: invite group
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="inviteGroupDialog" :visible.sync="inviteGroupDialog.visible" title="Invite To Group" width="450px")
div(v-if="inviteGroupDialog.visible" v-loading="inviteGroupDialog.loading")
span The limits for this is unknown be careful using it
el-select(v-model="inviteGroupDialog.groupId" clearable placeholder="Choose Group" filterable :disabled="inviteGroupDialog.loading" @change="isAllowedToInviteToGroup" style="margin-top:15px")
el-option-group(v-if="inviteGroupDialog.groups.length" label="Groups" style="width:410px")
el-option.x-friend-item(v-for="group in inviteGroupDialog.groups" :key="group.id" :label="group.name" :value="group.id" style="height:auto")
.avatar
img(v-lazy="group.iconUrl")
.detail
span.name(v-text="group.name")
el-select(v-model="inviteGroupDialog.userIds" multiple clearable placeholder="Choose Friends" filterable :disabled="inviteGroupDialog.loading" style="width:100%;margin-top:15px")
el-option-group(v-if="inviteGroupDialog.userId" label="Selected User")
el-option.x-friend-item(:key="inviteGroupDialog.userObject.id" :label="inviteGroupDialog.userObject.displayName" :value="inviteGroupDialog.userObject.id" style="height:auto")
template(v-if="inviteGroupDialog.userObject.id")
.avatar(:class="userStatusClass(inviteGroupDialog.userObject)")
img(v-lazy="userImage(inviteGroupDialog.userObject)")
.detail
span.name(v-text="inviteGroupDialog.userObject.displayName" :style="{'color':inviteGroupDialog.userObject.$userColour}")
span(v-else v-text="inviteGroupDialog.userId")
el-option-group(v-if="friendsGroup0.length" label="VIP")
el-option.x-friend-item(v-for="friend in friendsGroup0" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
template(v-if="friend.ref")
.avatar(:class="userStatusClass(friend.ref)")
img(v-lazy="userImage(friend.ref)")
.detail
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
span(v-else v-text="friend.id")
el-option-group(v-if="friendsGroup1.length" label="ONLINE")
el-option.x-friend-item(v-for="friend in friendsGroup1" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
template(v-if="friend.ref")
.avatar(:class="userStatusClass(friend.ref)")
img(v-lazy="userImage(friend.ref)")
.detail
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
span(v-else v-text="friend.id")
el-option-group(v-if="friendsGroup2.length" label="ACTIVE")
el-option.x-friend-item(v-for="friend in friendsGroup2" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
template(v-if="friend.ref")
.avatar
img(v-lazy="userImage(friend.ref)")
.detail
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
span(v-else v-text="friend.id")
el-option-group(v-if="friendsGroup3.length" label="OFFLINE")
el-option.x-friend-item(v-for="friend in friendsGroup3" :key="friend.id" :label="friend.name" :value="friend.id" style="height:auto")
template(v-if="friend.ref")
.avatar
img(v-lazy="userImage(friend.ref)")
.detail
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}")
span(v-else v-text="friend.id")
template(#footer)
el-button(type="primary" size="small" :disabled="inviteGroupDialog.loading || !inviteGroupDialog.userIds.length" @click="sendGroupInvite()") Invite
//- dialog: open source software notice
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" :visible.sync="ossDialog" title="Open Source Software Notice" width="650px")
div(v-if="ossDialog" style="height:350px;overflow:hidden scroll;word-break:break-all")

View File

@@ -1750,6 +1750,12 @@ class Database {
);
return userId;
}
async fixBrokenGroupInvites() {
await sqliteService.executeNonQuery(
`DELETE FROM ${Database.userPrefix}_notifications WHERE type LIKE '%.%'`
);
}
}
var self = new Database();