Skip to content

Commit

Permalink
[NEW] Experimental Game Center (externalComponents implementation) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
d-gubert authored Mar 19, 2020
1 parent 2c729b6 commit 292a7c3
Show file tree
Hide file tree
Showing 28 changed files with 1,167 additions and 27 deletions.
2 changes: 1 addition & 1 deletion app/api/server/v1/groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMes
import { API } from '../api';

// Returns the private group subscription IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property
function findPrivateGroupByIdOrName({ params, userId, checkedArchived = true }) {
export function findPrivateGroupByIdOrName({ params, userId, checkedArchived = true }) {
if ((!params.roomId || !params.roomId.trim()) && (!params.roomName || !params.roomName.trim())) {
throw new Meteor.Error('error-room-param-not-provided', 'The parameter "roomId" or "roomName" is required');
}
Expand Down
51 changes: 51 additions & 0 deletions app/apps/assets/stylesheets/apps.css
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,57 @@
}
}

.rc-game {
&__list {
.rc-table-title {
width: 135px;
}
}

&__container {
height: calc(100% - 79px);
}

&__close {
position: absolute;
top: -30px;
right: -25px;

cursor: pointer;

color: #e9ebee;

font-size: 20px;

&:hover {
color: white;
}
}

&__main {
min-width: 400px;
height: 100%;
}
}

.rc-game-modal {
&__container {
margin: -14px -14px -18px;
}

&__main {
min-height: 730px;
}
}

.rc-icon.game-center__invite-players-icon.game-center__invite-players-icon--plus {
font-size: 18px;

&:hover {
color: #1d74f5;
}
}

@keyframes play90 {
0% {
right: -798px;
Expand Down
58 changes: 58 additions & 0 deletions app/apps/client/RealAppsEngineUIHost.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Meteor } from 'meteor/meteor';
import { Session } from 'meteor/session';
import { AppsEngineUIHost } from '@rocket.chat/apps-engine/client/AppsEngineUIHost';

import { Rooms } from '../../models/client';
import { APIClient } from '../../utils/client';
import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL';

export class RealAppsEngineUIHost extends AppsEngineUIHost {
constructor() {
super();

this._baseURL = document.baseURI.slice(0, -1);
}

getUserAvatarUrl(username) {
const avatarUrl = getUserAvatarURL(username);

if (!avatarUrl.startsWith('http') && !avatarUrl.startsWith('data')) {
return `${ this._baseURL }${ avatarUrl }`;
}

return avatarUrl;
}

async getClientRoomInfo() {
const { name: slugifiedName, _id: id } = Rooms.findOne(Session.get('openedRoom'));

let cachedMembers = [];
try {
const { members } = await APIClient.get('v1/groups.members', { roomId: id });

cachedMembers = members.map(({ _id, username }) => ({
id: _id,
username,
avatarUrl: this.getUserAvatarUrl(username),
}));
} catch (error) {
console.warn(error);
}

return {
id,
slugifiedName,
members: cachedMembers,
};
}

async getClientUserInfo() {
const { username, _id } = Meteor.user();

return {
id: _id,
username,
avatarUrl: this.getUserAvatarUrl(username),
};
}
}
69 changes: 69 additions & 0 deletions app/apps/client/gameCenter/gameCenter.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<template name="GameCenter">
{{#table onScroll=onTableScroll onResize=onTableResize onSort=onTableSort}}
<tbody class="rc-game__list">
<thead>
<tr>
<th class="js-sort rc-table-td--medium" data-sort="name">
<div class="table-fake-th">{{_ "Name"}}</div>
</th>
<th class="rc-table-td">
<div class="table-fake-th">{{_ "Description"}} </div>
</th>
</tr>
</thead>
{{#each games}}
<tr class="rc-game-center__game" data-name="{{name}}">
<td>
<div class="rc-table-wrapper">
{{#if icon}}
<div class="rc-table-avatar" style="background-image:url({{icon}})"></div>
{{/if}}
<div class="rc-table-info">
<span class="rc-table-title">
{{name}}
</span>
</div>
</div>
</td>
<td>
<div class="rc-table-wrapper">
<div class="rc-table-info">
<span class="rc-table-title">
{{#if summary}}
{{summary}}
{{else}}
{{description}}
{{/if}}
</span>
{{#if summary}}
<span class="rc-table-subtitle">
{{description}}
</span>
{{/if}}
</div>
</div>
</td>
<td>
<div class="rc-table-wrapper">
<div class="rc-table-info">
<button class="rc-tooltip game-center__invite-players js-invite" aria-label="Invite Friends">
{{> icon block="game-center__invite-players-icon" icon="plus"}}
</button>
</div>
</div>
</td>
</tr>
{{/each}}
{{#if isLoading}}
<tr>
<td colspan="3" style="position: relative;">{{> loading}}</td>
</tr>
{{/if}}
</tbody>
{{/table}}
<div class="rc-user-info-container flex-nav animated{{#unless showGame}} animated-hidden{{/unless}}">
{{#if showGame}}
{{> GameContainer (gameContainerOptions) }}
{{/if}}
</div>
</template>
110 changes: 110 additions & 0 deletions app/apps/client/gameCenter/gameCenter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import toastr from 'toastr';
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';

import { modal } from '../../../ui-utils/client';
import { APIClient, t } from '../../../utils/client';

const getExternalComponents = async (instance) => {
try {
const { externalComponents } = await APIClient.get('apps/externalComponents');
instance.games.set(externalComponents);
} catch (e) {
toastr.error((e.xhr.responseJSON && e.xhr.responseJSON.error) || e.message);
}

instance.isLoading.set(false);
instance.ready.set(true);
};

const openGame = (gameManifestInfo) => {
const instance = Template.instance();
const { location = 'MODAL' } = gameManifestInfo;

if (location === 'CONTEXTUAL_BAR') {
instance.gameManifestInfo.set(gameManifestInfo);
} else if (location === 'MODAL') {
modal.open({
allowOutsideClick: false,
data: {
game: gameManifestInfo,
},
template: 'GameContainer',
type: 'rc-game',
});
}
};

Template.GameCenter.helpers({
isReady() {
if (Template.instance().ready != null) {
return Template.instance().ready.get();
}
return false;
},
games() {
return Template.instance().games.get();
},
isLoading() {
return Template.instance().isLoading.get();
},
onTableScroll() {
const instance = Template.instance();
if (instance.loading || instance.end.get()) {
return;
}
return function(currentTarget) {
if (currentTarget.offsetHeight + currentTarget.scrollTop >= currentTarget.scrollHeight - 100) {
return instance.page.set(instance.page.get() + 1);
}
};
},
showGame() {
return Template.instance().gameManifestInfo.get();
},
gameContainerOptions() {
const { gameManifestInfo, clearGameManifestInfo } = Template.instance();

return {
game: gameManifestInfo.get(),
showBackButton: true,
clearGameManifestInfo,
};
},
});

Template.GameCenter.onCreated(function() {
this.ready = new ReactiveVar(false);
this.games = new ReactiveVar([]);
this.isLoading = new ReactiveVar(true);
this.page = new ReactiveVar(0);
this.end = new ReactiveVar(false);

this.gameManifestInfo = new ReactiveVar(null);

this.clearGameManifestInfo = () => {
this.gameManifestInfo.set(null);
};

getExternalComponents(this);
});

Template.GameCenter.events({
'click .rc-game-center__game'() {
const gameManifestInfo = this;

openGame(gameManifestInfo);
},
'click .js-invite'(event) {
event.stopPropagation();
modal.open({
title: t('Invite You Friends to Join'),
content: 'InvitePlayers',
data: this,
confirmOnEnter: false,
showCancelButton: false,
showConfirmButton: false,
html: false,
});
},
});
30 changes: 30 additions & 0 deletions app/apps/client/gameCenter/gameContainer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<template name="GameContainer">
{{#if isContextualBar}}
<header class="contextual-bar__header">
<div class="contextual-bar__header-data">
{{#if showBackButton}}
<button
class="rc-button rc-button--nude contextual-bar__header-back-btn js-back"
title="{{_ 'Back_to_Game_Center'}}"
>
<i class="icon-angle-left"></i>
</button>
{{/if}}
<img class="rc-table-avatar contextual-bar__header-icon" src="{{ game.icon }}">
<h1 class="contextual-bar__header-title">{{ game.name }}</h1>
</div>
<button class="contextual-bar__header-close js-close">
{{> icon block="contextual-bar__header-close-icon" icon="plus"}}
</button>
</header>
{{/if}}

{{#if isLoading}}
{{> loading}}
{{else}}
<div class="rc-game__container {{#if isModal}} rc-game-modal__container {{/if}}">
<div class="rc-game__close"></div>
<iframe class="rc-game__main {{#if isModal}} rc-game-modal__main {{/if}}" src="{{ game.url }}"></iframe>
</div>
{{/if}}
</template>
Loading

0 comments on commit 292a7c3

Please sign in to comment.