Better support for secure instances

This commit is contained in:
GotoFinal
2022-09-09 17:16:07 +02:00
committed by Natsumi
parent c937a809e6
commit 83788e6317
2 changed files with 180 additions and 119 deletions
+160 -99
View File
@@ -208,14 +208,20 @@ speechSynthesis.getVoices();
$appDarkStyle.href = `app.dark.css?_=${Date.now()}`; $appDarkStyle.href = `app.dark.css?_=${Date.now()}`;
document.head.appendChild($appDarkStyle); document.head.appendChild($appDarkStyle);
var getLaunchURL = function (worldId, instanceId) { var getLaunchURL = function (instance) {
if (instanceId) { var L = instance;
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent( if (L.instanceId) {
worldId if (L.shortName.length > 0 && L.userId != null) {
)}&instanceId=${encodeURIComponent(instanceId)}`; return `https://vrchat.com/home/launch?worldId=${
encodeURIComponent(L.worldId)
}&instanceId=${encodeURIComponent(L.instanceId)}&shortName=${encodeURIComponent(L.shortName)}`;
} }
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent( return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
worldId L.worldId
)}&instanceId=${encodeURIComponent(L.instanceId)}`;
}
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
L.worldId
)}`; )}`;
}; };
@@ -669,6 +675,7 @@ speechSynthesis.getVoices();
instanceName: '', instanceName: '',
accessType: '', accessType: '',
region: '', region: '',
shortName: '',
userId: null, userId: null,
hiddenId: null, hiddenId: null,
privateId: null, privateId: null,
@@ -684,6 +691,13 @@ speechSynthesis.getVoices();
ctx.isTraveling = true; ctx.isTraveling = true;
} else if (_tag.startsWith('local') === false) { } else if (_tag.startsWith('local') === false) {
var sep = _tag.indexOf(':'); var sep = _tag.indexOf(':');
// technically not part of instance id, but might be there when coping id from url so why not support it
var shortNameQualifier = "&shortName="
var shortNameIndex = _tag.indexOf(shortNameQualifier)
if (shortNameIndex >= 0) {
ctx.shortName = _tag.substr(shortNameIndex + shortNameQualifier.length)
_tag = _tag.substr(0, shortNameIndex);
}
if (sep >= 0) { if (sep >= 0) {
ctx.worldId = _tag.substr(0, sep); ctx.worldId = _tag.substr(0, sep);
ctx.instanceId = _tag.substr(sep + 1); ctx.instanceId = _tag.substr(sep + 1);
@@ -768,7 +782,8 @@ speechSynthesis.getVoices();
template: template:
'<el-button @click="confirm" size="mini" icon="el-icon-message" circle></el-button>', '<el-button @click="confirm" size="mini" icon="el-icon-message" circle></el-button>',
props: { props: {
location: String location: String,
shortname: String
}, },
methods: { methods: {
parse() { parse() {
@@ -777,7 +792,7 @@ speechSynthesis.getVoices();
: 'none'; : 'none';
}, },
confirm() { confirm() {
$app.selfInvite(this.location); $app.selfInvite(this.location, this.shortname);
} }
}, },
watch: { watch: {
@@ -1977,6 +1992,9 @@ speechSynthesis.getVoices();
json, json,
params params
}; };
if (args.json) {
args.shortOrSecureName = args.json.shortName ?? args.json.secureName;
}
this.$emit('INSTANCE:SHORTNAME', args); this.$emit('INSTANCE:SHORTNAME', args);
return args; return args;
}); });
@@ -1992,7 +2010,8 @@ speechSynthesis.getVoices();
return this.call( return this.call(
`invite/myself/to/${params.worldId}:${params.instanceId}`, `invite/myself/to/${params.worldId}:${params.instanceId}`,
{ {
method: 'POST' method: 'POST',
params
} }
).then((json) => { ).then((json) => {
var args = { var args = {
@@ -10145,7 +10164,7 @@ speechSynthesis.getVoices();
} }
switch (L.accessType) { switch (L.accessType) {
case 'public': case 'public':
L.joinUrl = getLaunchURL(L.worldId, L.instanceId); L.joinUrl = getLaunchURL(L);
L.accessName = `Public #${L.instanceName} (${platform})`; L.accessName = `Public #${L.instanceName} (${platform})`;
break; break;
case 'invite+': case 'invite+':
@@ -12453,22 +12472,12 @@ speechSynthesis.getVoices();
inputErrorMessage: 'World URL/ID is required', inputErrorMessage: 'World URL/ID is required',
callback: (action, instance) => { callback: (action, instance) => {
if (action === 'confirm' && instance.inputValue) { if (action === 'confirm' && instance.inputValue) {
var testUrl = instance.inputValue.substring(0, 15); if (!this.directAccessWorld(instance.inputValue)) {
if (testUrl === 'https://vrchat.') {
var worldInstance = this.parseLocationUrl(
instance.inputValue
);
if (worldInstance) {
this.showWorldDialog(worldInstance);
} else {
this.$message({ this.$message({
message: 'Invalid URL', message: 'Invalid URL/id',
type: 'error' type: 'error'
}); });
} }
} else {
this.showWorldDialog(instance.inputValue);
}
} }
} }
}); });
@@ -12535,32 +12544,53 @@ speechSynthesis.getVoices();
}); });
}; };
$app.methods.directAccessParse = function (input) { $app.methods.directAccessWorld = function (input) {
var testUrl = input.substring(0, 15); if (input.startsWith("/home/")) {
if (testUrl === 'https://vrch.at') { input = "https://vrchat.com" + input;
return AppApi.FollowUrl(input).then((output) => {
var url = output;
// /home/launch?worldId=wrld_f20326da-f1ac-45fc-a062-609723b097b1&instanceId=33570~region(jp)&shortName=cough-stockinglinz-ddd26
// https://vrch.at/wrld_f20326da-f1ac-45fc-a062-609723b097b1
if (url.substring(0, 18) === 'https://vrchat.com') {
url = url.substring(18);
} }
if (url.substring(0, 13) === '/home/launch?') { if (input.startsWith('https://vrch.at')) {
var urlParams = new URLSearchParams(url.substring(13)); return AppApi.FollowUrl(input).then((output) => {
return this.directAccessWorld(output);
});
} else if (input.startsWith('https://vrchat.') || input.startsWith("/home/")) {
var url = new URL(input);
var urlPath = url.pathname;
if (urlPath.substring(5, 12) === '/world/') {
var worldId = urlPath.substring(12);
this.showWorldDialog(worldId);
return true;
} else if (urlPath.substring(5, 12) === '/launch') {
var urlParams = new URLSearchParams(url.search);
var worldId = urlParams.get('worldId'); var worldId = urlParams.get('worldId');
var instanceId = urlParams.get('instanceId'); var instanceId = urlParams.get('instanceId');
if (instanceId) { if (instanceId) {
var shortName = urlParams.get('shortName');
var location = `${worldId}:${instanceId}`; var location = `${worldId}:${instanceId}`;
this.showWorldDialog(location); this.showWorldDialog(location, shortName);
return true; return true;
} else if (worldId) { } else if (worldId) {
this.showWorldDialog(worldId); this.showWorldDialog(worldId);
return true; return true;
} }
} }
} else if (input.substring(0, 5) === 'wrld_') {
// a bit hacky, but supports weird malformed inputs cut out from url, why not
if (input.indexOf("&instanceId=") >= 0) {
input = "https://vrchat.com/home/launch?worldId=" + input;
return this.directAccessWorld(input);
}
this.showWorldDialog(input.trim());
return true;
}
return false; return false;
}); };
} else if (testUrl === 'https://vrchat.') {
$app.methods.directAccessParse = function (input) {
var testUrl = input.substring(0, 15);
if (this.directAccessWorld(input)) {
return true;
}
if (testUrl === 'https://vrchat.') {
var url = new URL(input); var url = new URL(input);
var urlPath = url.pathname; var urlPath = url.pathname;
if (urlPath.substring(5, 11) === '/user/') { if (urlPath.substring(5, 11) === '/user/') {
@@ -12571,29 +12601,10 @@ speechSynthesis.getVoices();
var avatarId = urlPath.substring(13); var avatarId = urlPath.substring(13);
this.showAvatarDialog(avatarId); this.showAvatarDialog(avatarId);
return true; return true;
} else if (urlPath.substring(5, 12) === '/world/') {
var worldId = urlPath.substring(12);
this.showWorldDialog(worldId);
return true;
} else if (urlPath.substring(5, 12) === '/launch') {
var urlParams = new URLSearchParams(url.search);
var worldId = urlParams.get('worldId');
var instanceId = urlParams.get('instanceId');
if (instanceId) {
var location = `${worldId}:${instanceId}`;
this.showWorldDialog(location);
return true;
} else if (worldId) {
this.showWorldDialog(worldId);
return true;
}
} }
} else if (input.substring(0, 4) === 'usr_') { } else if (input.substring(0, 4) === 'usr_') {
this.showUserDialog(input.trim()); this.showUserDialog(input.trim());
return true; return true;
} else if (input.substring(0, 5) === 'wrld_') {
this.showWorldDialog(input.trim());
return true;
} else if (input.substring(0, 5) === 'avtr_') { } else if (input.substring(0, 5) === 'avtr_') {
this.showAvatarDialog(input.trim()); this.showAvatarDialog(input.trim());
return true; return true;
@@ -14335,13 +14346,16 @@ speechSynthesis.getVoices();
D.isFavorite = false; D.isFavorite = false;
}); });
$app.methods.showWorldDialog = function (tag) { $app.methods.showWorldDialog = function (tag, shortName = void 0) {
this.$nextTick(() => adjustDialogZ(this.$refs.worldDialog.$el)); this.$nextTick(() => adjustDialogZ(this.$refs.worldDialog.$el));
var D = this.worldDialog; var D = this.worldDialog;
var L = API.parseLocation(tag); var L = API.parseLocation(tag);
if (L.worldId === '') { if (L.worldId === '') {
return; return;
} }
if (!L.shortName && shortName) {
L.shortName = shortName;
}
if (L.worldId === L.instanceId) { if (L.worldId === L.instanceId) {
// very janky fix for removing empty worldId instance // very janky fix for removing empty worldId instance
L.instanceId = ''; L.instanceId = '';
@@ -14438,12 +14452,13 @@ speechSynthesis.getVoices();
users: [] users: []
}; };
} }
var {instanceId} = D.$location; var {instanceId, shortName} = D.$location;
if (instanceId && typeof instances[instanceId] === 'undefined') { if (instanceId && typeof instances[instanceId] === 'undefined') {
instances[instanceId] = { instances[instanceId] = {
id: instanceId, id: instanceId,
occupants: 0, occupants: 0,
friendCount: 0, friendCount: 0,
shortName: shortName,
users: [] users: []
}; };
} }
@@ -14516,6 +14531,9 @@ speechSynthesis.getVoices();
// this should be block scope variable // this should be block scope variable
const L = API.parseLocation(`${D.id}:${instance.id}`); const L = API.parseLocation(`${D.id}:${instance.id}`);
instance.location = L.tag; instance.location = L.tag;
if (!L.shortName) {
L.shortName = instance.shortName;
}
instance.$location = L; instance.$location = L;
if (L.userId) { if (L.userId) {
var ref = API.cachedUsers.get(L.userId); var ref = API.cachedUsers.get(L.userId);
@@ -15143,7 +15161,8 @@ speechSynthesis.getVoices();
var L = API.parseLocation(D.worldId); var L = API.parseLocation(D.worldId);
API.selfInvite({ API.selfInvite({
instanceId: L.instanceId, instanceId: L.instanceId,
worldId: L.worldId worldId: L.worldId,
shortName: L.shortName
}).finally(inviteLoop); }).finally(inviteLoop);
} else { } else {
API.sendInvite( API.sendInvite(
@@ -15361,6 +15380,7 @@ speechSynthesis.getVoices();
region: '', region: '',
strict: false, strict: false,
location: '', location: '',
shortName: '',
url: '' url: ''
}; };
@@ -15412,11 +15432,12 @@ speechSynthesis.getVoices();
tags.push('~strict'); tags.push('~strict');
} }
D.instanceId = tags.join(''); D.instanceId = tags.join('');
this.updateNewInstanceDialog(false);
}; };
$app.methods.selfInvite = function (location) { $app.methods.selfInvite = function (location, shortName = void 0) {
var L = API.parseLocation(location); var L = API.parseLocation(location);
if (L.isOffline || L.isPrivate || L.isTraveling || L.worldId === '') { if (L.isOffline || L.isTraveling || L.worldId === '') {
return; return;
} }
if (API.currentUser.status === 'busy') { if (API.currentUser.status === 'busy') {
@@ -15429,7 +15450,8 @@ speechSynthesis.getVoices();
} }
API.selfInvite({ API.selfInvite({
instanceId: L.instanceId, instanceId: L.instanceId,
worldId: L.worldId worldId: L.worldId,
shortName: shortName ?? L.shortName
}).then((args) => { }).then((args) => {
this.$message({ this.$message({
message: 'Self invite sent', message: 'Self invite sent',
@@ -15439,15 +15461,22 @@ speechSynthesis.getVoices();
}); });
}; };
var updateLocationURL = function () { $app.methods.updateNewInstanceDialog = function (noChanges = false) {
var D = $app.newInstanceDialog; var D = $app.newInstanceDialog;
if (D.instanceId) { if (D.instanceId) {
D.location = `${D.worldId}:${D.instanceId}`; D.location = `${D.worldId}:${D.instanceId}`;
} else { } else {
D.location = D.worldId; D.location = D.worldId;
} }
D.url = getLaunchURL(D.worldId, D.instanceId); var L = API.parseLocation(D.location);
if (noChanges) {
L.shortName = D.shortName;
} else {
D.shortName = '';
}
D.url = getLaunchURL(L);
}; };
var saveNewInstanceDialog = function () { var saveNewInstanceDialog = function () {
configRepository.setString( configRepository.setString(
'instanceDialogAccessType', 'instanceDialogAccessType',
@@ -15474,9 +15503,8 @@ speechSynthesis.getVoices();
this.newInstanceDialog.strict this.newInstanceDialog.strict
); );
$app.buildInstance(); $app.buildInstance();
updateLocationURL();
}; };
$app.watch['newInstanceDialog.worldId'] = updateLocationURL; $app.watch['newInstanceDialog.worldId'] = $app.methods.updateNewInstanceDialog;
$app.watch['newInstanceDialog.instanceName'] = saveNewInstanceDialog; $app.watch['newInstanceDialog.instanceName'] = saveNewInstanceDialog;
$app.watch['newInstanceDialog.accessType'] = saveNewInstanceDialog; $app.watch['newInstanceDialog.accessType'] = saveNewInstanceDialog;
$app.watch['newInstanceDialog.region'] = saveNewInstanceDialog; $app.watch['newInstanceDialog.region'] = saveNewInstanceDialog;
@@ -15515,7 +15543,9 @@ speechSynthesis.getVoices();
// if (configRepository.getBool('instanceDialogStrict') !== null) { // if (configRepository.getBool('instanceDialogStrict') !== null) {
// D.strict = configRepository.getBool('instanceDialogStrict'); // D.strict = configRepository.getBool('instanceDialogStrict');
// } // }
D.shortName = '';
this.buildInstance(); this.buildInstance();
this.updateNewInstanceDialog();
D.visible = true; D.visible = true;
}; };
@@ -15675,6 +15705,7 @@ speechSynthesis.getVoices();
desktop: configRepository.getBool('launchAsDesktop'), desktop: configRepository.getBool('launchAsDesktop'),
location: '', location: '',
url: '', url: '',
shortName: '',
shortUrl: '' shortUrl: ''
}; };
@@ -15688,12 +15719,29 @@ speechSynthesis.getVoices();
API.$on('INSTANCE:SHORTNAME', function (args) { API.$on('INSTANCE:SHORTNAME', function (args) {
var url = ''; var url = '';
if (args.json && args.json.shortName) { var shortName = '';
url = `https://vrch.at/${args.json.shortName}`; if (args.json && args.shortOrSecureName) {
shortName = args.shortOrSecureName;
url = `https://vrch.at/${shortName}`;
} }
$app.newInstanceDialog.shortName = shortName;
$app.launchDialog.shortName = shortName;
$app.launchDialog.shortUrl = url; $app.launchDialog.shortUrl = url;
$app.launchDialog.url = $app.addShortNameToFullUrl($app.launchDialog.url, shortName);
$app.updateNewInstanceDialog(true);
}); });
$app.methods.addShortNameToFullUrl = function(input, shortName) {
if (input.trim().length == 0) {
return input;
}
var url = new URL(input);
var urlParams = new URLSearchParams(url.search);
urlParams.set("shortName", shortName);
url.search = urlParams.toString();
return url.toString();
}
$app.methods.showLaunchDialog = function (tag) { $app.methods.showLaunchDialog = function (tag) {
this.$nextTick(() => adjustDialogZ(this.$refs.launchDialog.$el)); this.$nextTick(() => adjustDialogZ(this.$refs.launchDialog.$el));
var L = API.parseLocation(tag); var L = API.parseLocation(tag);
@@ -15707,13 +15755,13 @@ speechSynthesis.getVoices();
D.location = L.worldId; D.location = L.worldId;
} }
D.shortUrl = ''; D.shortUrl = '';
D.url = getLaunchURL(L.worldId, L.instanceId); D.url = getLaunchURL(L);
D.visible = true; D.visible = true;
if (L.userId === API.currentUser.id) { if (L.userId != null) {
API.getInstanceShortName({ API.getInstanceShortName({
worldId: L.worldId, worldId: L.worldId,
instanceId: L.instanceId instanceId: L.instanceId
}); })
} }
}; };
@@ -15754,24 +15802,39 @@ speechSynthesis.getVoices();
document.getElementById('copy_to_clipboard').remove(); document.getElementById('copy_to_clipboard').remove();
}; };
$app.methods.copyInstanceUrl = function (url) { $app.methods.copyInstanceUrlAndUpdateNewInstanceDialog = function (url) {
this.copyToClipboard(url); this.copyInstanceUrl(url)
this.$message({ .then(url => {
message: 'Instance URL copied to clipboard', $app.newInstanceDialog.url = url;
type: 'success' $app.updateNewInstanceDialog(true)
}); });
this.launchDialog.visible = false; }
this.newInstanceDialog.visible = false;
};
$app.methods.copyLocation = function (location) { $app.methods.copyInstanceUrl = function (url) {
var L = API.parseLocation(location); var copyUrl = function (url) {
var url = getLaunchURL(L.worldId, L.instanceId); $app.copyToClipboard(url);
this.copyToClipboard(url); $app.$message({
this.$message({
message: 'Instance URL copied to clipboard', message: 'Instance URL copied to clipboard',
type: 'success' type: 'success'
}); });
return url;
}
var location = this.parseLocationUrl(url)
if (!location.shortName) {
return API.getInstanceShortName({
worldId: location.worldId,
instanceId: location.instanceId
}).then((args) => {
if (args.json && args.shortOrSecureName) {
var newUrl = this.addShortNameToFullUrl(url, args.shortOrSecureName)
return copyUrl(newUrl);
} else {
return copyUrl(url);
}
});
}
copyUrl(url);
return Promise.resolve(url);
}; };
$app.methods.copyAvatarId = function (avatarId) { $app.methods.copyAvatarId = function (avatarId) {
@@ -15814,14 +15877,6 @@ speechSynthesis.getVoices();
this.copyToClipboard(`https://vrchat.com/home/user/${userId}`); this.copyToClipboard(`https://vrchat.com/home/user/${userId}`);
}; };
$app.methods.copyInstanceUrl = function (url) {
this.$message({
message: 'Instance URL copied to clipboard',
type: 'success'
});
this.copyToClipboard(url);
};
$app.methods.copyText = function (text) { $app.methods.copyText = function (text) {
this.$message({ this.$message({
message: 'Text copied to clipboard', message: 'Text copied to clipboard',
@@ -16479,7 +16534,8 @@ speechSynthesis.getVoices();
var L = API.parseLocation(J.worldId); var L = API.parseLocation(J.worldId);
API.selfInvite({ API.selfInvite({
instanceId: L.instanceId, instanceId: L.instanceId,
worldId: L.worldId worldId: L.worldId,
shortName: L.shortName
}).finally(inviteLoop); }).finally(inviteLoop);
} else if ($app.uploadImage) { } else if ($app.uploadImage) {
API.sendInvitePhoto( API.sendInvitePhoto(
@@ -16649,7 +16705,8 @@ speechSynthesis.getVoices();
var L = API.parseLocation(J.worldId); var L = API.parseLocation(J.worldId);
API.selfInvite({ API.selfInvite({
instanceId: L.instanceId, instanceId: L.instanceId,
worldId: L.worldId worldId: L.worldId,
shortName: L.shortName
}).finally(inviteLoop); }).finally(inviteLoop);
} else if ($app.uploadImage) { } else if ($app.uploadImage) {
API.sendInvitePhoto( API.sendInvitePhoto(
@@ -18507,11 +18564,18 @@ speechSynthesis.getVoices();
var urlParams = new URLSearchParams(url.search); var urlParams = new URLSearchParams(url.search);
var worldId = urlParams.get('worldId'); var worldId = urlParams.get('worldId');
var instanceId = urlParams.get('instanceId'); var instanceId = urlParams.get('instanceId');
var shortName = urlParams.get('shortName');
var location;
if (instanceId) { if (instanceId) {
return `${worldId}:${instanceId}`; location = `${worldId}:${instanceId}`;
} else if (worldId) {
location = worldId;
} }
if (worldId) { return {
return worldId; worldId: worldId,
instanceId: instanceId,
location: location,
shortName: shortName
} }
} }
return void 0; return void 0;
@@ -18843,9 +18907,6 @@ speechSynthesis.getVoices();
if (L.accessType === 'friends' && !this.friends.has(L.userId)) { if (L.accessType === 'friends' && !this.friends.has(L.userId)) {
return false; return false;
} }
if (L.accessType === 'invite' || L.accessType === 'invite+') {
return false;
}
return true; return true;
}; };
+4 -4
View File
@@ -1543,7 +1543,7 @@ html
el-tooltip(placement="top" content="Launch/Invite" :disabled="hideTooltips") el-tooltip(placement="top" content="Launch/Invite" :disabled="hideTooltips")
launch(:location="userDialog.$location.tag" style="margin-left:5px") launch(:location="userDialog.$location.tag" style="margin-left:5px")
el-tooltip(placement="top" content="Invite yourself" :disabled="hideTooltips") el-tooltip(placement="top" content="Invite yourself" :disabled="hideTooltips")
invite-yourself(:location="userDialog.$location.tag" style="margin-left:5px") invite-yourself(:locaTion="userDialog.$location.tag" :shortname="userDialog.$location.shortName" style="margin-left:5px")
el-tooltip(placement="top" content="Refresh player count" :disabled="hideTooltips") el-tooltip(placement="top" content="Refresh player count" :disabled="hideTooltips")
el-button(v-if="userDialog.$location.tag !== lastLocation.location" @click="refreshInstancePlayerCount(userDialog.$location.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle) el-button(v-if="userDialog.$location.tag !== lastLocation.location" @click="refreshInstancePlayerCount(userDialog.$location.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle)
span(v-if="userDialog.instance.occupants" style="margin-left:5px") {{ userDialog.instance.occupants }} #[template(v-if="userDialog.instance.friendCount > 0") ({{ userDialog.instance.friendCount }})] span(v-if="userDialog.instance.occupants" style="margin-left:5px") {{ userDialog.instance.occupants }} #[template(v-if="userDialog.instance.friendCount > 0") ({{ userDialog.instance.friendCount }})]
@@ -1786,7 +1786,7 @@ html
span.flags(v-else class="us" style="display:inline-block;margin-left:5px") span.flags(v-else class="us" style="display:inline-block;margin-left:5px")
i.el-icon-lock(v-if="room.$location.strict" style="display:inline-block;margin-left:5px") i.el-icon-lock(v-if="room.$location.strict" style="display:inline-block;margin-left:5px")
el-tooltip(placement="top" content="Invite yourself" :disabled="hideTooltips") el-tooltip(placement="top" content="Invite yourself" :disabled="hideTooltips")
invite-yourself(:location="room.$location.tag" style="margin-left:5px") invite-yourself(:location="room.$location.tag" :shortname="room.$location.shortName" style="margin-left:5px")
el-tooltip(placement="top" content="Refresh player count" :disabled="hideTooltips") el-tooltip(placement="top" content="Refresh player count" :disabled="hideTooltips")
el-button(v-if="room.$location.tag !== lastLocation.location" @click="refreshInstancePlayerCount(room.$location.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle) el-button(v-if="room.$location.tag !== lastLocation.location" @click="refreshInstancePlayerCount(room.$location.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle)
span(v-if="room.occupants" style="margin-left:5px") {{ room.occupants }} #[template(v-if="room.friendCount > 0") ({{ room.friendCount }})] span(v-if="room.occupants" style="margin-left:5px") {{ room.occupants }} #[template(v-if="room.friendCount > 0") ({{ room.friendCount }})]
@@ -2138,11 +2138,11 @@ html
span.name(v-text="friend.ref.displayName" :style="{'color':friend.ref.$userColour}") 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="Location") el-form-item(label="Location")
el-input(v-model="newInstanceDialog.location" size="mini" readonly @click.native="$event.target.tagName === 'INPUT' && $event.target.select()") el-input(v-model="newInstanceDialog.location" size="mini" readonly)
el-form-item(label="URL") el-form-item(label="URL")
el-input(v-model="newInstanceDialog.url" size="mini" readonly @click.native="$event.target.tagName === 'INPUT' && $event.target.select()") el-input(v-model="newInstanceDialog.url" size="mini" readonly @click.native="$event.target.tagName === 'INPUT' && $event.target.select()")
template(#footer) template(#footer)
el-button(size="small" @click="copyInstanceUrl(newInstanceDialog.url)") Copy URL el-button(size="small" @click="copyInstanceUrlAndUpdateNewInstanceDialog(newInstanceDialog.url)") Copy URL
el-button(size="small" @click="selfInvite(newInstanceDialog.location)") Self Invite el-button(size="small" @click="selfInvite(newInstanceDialog.location)") Self Invite
el-button(size="small" @click="showInviteDialog(newInstanceDialog.location)" :disabled="(newInstanceDialog.accessType === 'friends' || newInstanceDialog.accessType === 'invite') && newInstanceDialog.userId !== API.currentUser.id") Invite el-button(size="small" @click="showInviteDialog(newInstanceDialog.location)" :disabled="(newInstanceDialog.accessType === 'friends' || newInstanceDialog.accessType === 'invite') && newInstanceDialog.userId !== API.currentUser.id") Invite
el-button(type="primary" size="small" @click="showLaunchDialog(newInstanceDialog.location)") Launch el-button(type="primary" size="small" @click="showLaunchDialog(newInstanceDialog.location)") Launch