Skip to content

Commit 8b1349b

Browse files
authored
Merge pull request #655 from Peergos/feat/template-app-support
support template apps
2 parents 607db8c + fb5d444 commit 8b1349b

File tree

11 files changed

+681
-108
lines changed

11 files changed

+681
-108
lines changed

src/components/Group.vue

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
</Prompt>
1515
<div class="modal-header">
1616
<span>
17-
<h4 style="text-align: center;" @click="changeGroupTitle()">{{ displayedTitle }}&nbsp;&nbsp;<i v-if="isAdmin" @click="changeGroupTitle()" class="fa fa-edit" aria-hidden="true"></i></h4>
17+
<h4 style="text-align: center;" @click="changeGroupTitle()">{{ displayedTitle }}&nbsp;&nbsp;<i v-if="isAdmin && allowTitleChange" @click="changeGroupTitle()" class="fa fa-edit" aria-hidden="true"></i></h4>
1818
</span>
1919
</div>
2020

@@ -124,14 +124,15 @@ module.exports = {
124124
prompt_consumer_func: () => {},
125125
displayedTitle: "",
126126
updateLabel: "Apply Changes",
127-
addLabel: "Invite to Chat",
127+
addLabel: "Invite",
128128
genericLabel: "chat",
129129
isAdmin: false,
130-
memberAccess: "Member"
130+
memberAccess: "Member",
131+
allowTitleChange: true,
131132
}
132133
},
133134
props: ['existingGroups', 'groupId', 'groupTitle', 'existingGroupMembers', 'friendNames'
134-
, 'updatedGroupMembership', 'existingAdmins'],
135+
, 'updatedGroupMembership', 'existingAdmins', 'isTemplateApp'],
135136
computed: {
136137
...Vuex.mapState([
137138
'context',
@@ -143,6 +144,9 @@ module.exports = {
143144
this.updateLabel = "Create";
144145
}
145146
this.isAdmin = this.existingAdmins.findIndex(v => v === this.context.username) > -1;
147+
if (this.isTemplateApp) {
148+
this.allowTitleChange = false;
149+
}
146150
},
147151
methods: {
148152
updateGroupMembership: function () {
@@ -164,7 +168,7 @@ module.exports = {
164168
}
165169
},
166170
changeGroupTitle: function () {
167-
if (!this.isAdmin) {
171+
if (!this.isAdmin || !this.allowTitleChange) {
168172
return;
169173
}
170174
let that = this;
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
<template>
2+
<transition name="modal" appear>
3+
<div class="app-template-prompt app-modal__overlay" @click="closePrompt()">
4+
5+
<div class="app-template-prompt__container" @click.stop>
6+
<header class="template-prompt__header">
7+
<AppButton class="close" icon="close" @click.native="closePrompt()"/>
8+
<h3>{{message}}</h3>
9+
</header>
10+
<div class="modal-body" style="padding: 15px;">
11+
<div class="flex-thumbnail-container">
12+
<div style="padding:20px;">
13+
<img id="profile-image" alt="Profile image" v-if="hasAppIcon()" style="width:128px; height:128px" v-bind:src="getAppIcon()"/>
14+
</div>
15+
<div class="flex-image-button-container">
16+
<div class="flex-container">
17+
<button class="btn btn-success flex-grow" @click="triggerUpload">Set Icon</button>
18+
<input type="file" id="uploadImageInput" @change="uploadImageFile" style="display:none;" accept="image/*" />
19+
</div>
20+
</div>
21+
</div>
22+
</div>
23+
<div v-if="maxLength > 0" class="template-prompt__body" style="margin: 20px;">
24+
<input
25+
v-if="placeholder"
26+
id="prompt-input"
27+
ref="prompt"
28+
v-model="prompt_result"
29+
type="text"
30+
:placeholder="placeholder"
31+
:maxlength="maxLength"
32+
@keyup.enter="getPrompt(this.prompt_result)"
33+
autofocus
34+
>
35+
</input>
36+
</div>
37+
<footer class="template-prompt__footer">
38+
<AppButton outline @click.native="closePrompt()">
39+
{{ translate("PROMPT.CANCEL") }}
40+
</AppButton>
41+
42+
<AppButton
43+
id='prompt-button-id'
44+
type="primary"
45+
accent
46+
@click.native="getPrompt(this.prompt_result)"
47+
>
48+
{{action}}
49+
</AppButton>
50+
</footer>
51+
</div>
52+
</div>
53+
</transition>
54+
</template>
55+
56+
<script>
57+
const AppButton = require("../AppButton.vue");
58+
const i18n = require("../../i18n/index.js");
59+
60+
module.exports = {
61+
components: {
62+
AppButton,
63+
},
64+
mixins:[i18n],
65+
data() {
66+
return {
67+
prompt_result: '',
68+
base64Image:'',
69+
}
70+
},
71+
props: {
72+
message: {
73+
type: String,
74+
default: ''
75+
},
76+
placeholder: {
77+
type: String,
78+
default: null
79+
},
80+
value:{
81+
type: String,
82+
default: ''
83+
},
84+
max_input_size:{
85+
type: Number,
86+
default: 255
87+
},
88+
consumer_func: {
89+
type: Function
90+
},
91+
action:{
92+
type: String,
93+
},
94+
appIconBase64Image:{
95+
type: String,
96+
},
97+
},
98+
computed: {
99+
maxLength() {
100+
if (this.max_input_size == -1) {
101+
return -1;
102+
}
103+
return (this.max_input_size == '') ? 32 : this.max_input_size;
104+
}
105+
},
106+
107+
mounted() {
108+
this.prompt_result = this.value;
109+
this.base64Image = this.appIconBase64Image;
110+
if(this.placeholder !== null && this.maxLength > 0){
111+
this.$refs.prompt.focus()
112+
}
113+
},
114+
115+
methods: {
116+
closePrompt() {
117+
this.consumer_func(null, null);
118+
this.$emit("hide-prompt");
119+
},
120+
121+
getPrompt() {
122+
this.consumer_func(this.prompt_result, this.base64Image);
123+
this.$emit("hide-prompt");
124+
},
125+
getAppIcon: function() {
126+
return this.base64Image;
127+
},
128+
hasAppIcon: function() {
129+
return this.base64Image.length > 0;
130+
},
131+
triggerUpload: function() {
132+
document.getElementById('uploadImageInput').click()
133+
},
134+
uploadImageFile: function(evt) {
135+
let files = evt.target.files || evt.dataTransfer.files;
136+
let file = files[0];
137+
let that = this;
138+
let filereader = new FileReader();
139+
filereader.file_name = file.name;
140+
let thumbnailWidth = 64;
141+
let thumbnailHeight = 64;
142+
filereader.onload = function(){
143+
let canvas = document.createElement("canvas");
144+
canvas.width = thumbnailWidth;
145+
canvas.height = thumbnailHeight;
146+
let context = canvas.getContext("2d");
147+
let image = new Image();
148+
image.onload = function() {
149+
try {
150+
context.drawImage(image, 0, 0, thumbnailWidth, thumbnailHeight);
151+
} catch (ex) {
152+
console.log("Unable to create icon. Maybe blocked by browser addon?");
153+
}
154+
that.base64Image = canvas.toDataURL();
155+
};
156+
image.onerror = function() {
157+
that.showMessage(true, that.translate("PROFILE.ERROR.IMAGE"));
158+
};
159+
image.src = this.result;
160+
};
161+
filereader.readAsDataURL(file);
162+
},
163+
}
164+
}
165+
166+
</script>
167+
168+
<style>
169+
.app-template-prompt.app-modal__overlay{
170+
display:flex;
171+
align-items: center;
172+
justify-content: center;
173+
}
174+
.app-template-prompt__container{
175+
width: 400px;
176+
padding: 16px;
177+
border-radius: 4px;
178+
color: var(--color);
179+
background-color:var(--bg);
180+
box-shadow: 0 6px 16px rgba(0,0,0,0.15);
181+
}
182+
.template-prompt__header h3{
183+
border-top:0;
184+
font-weight: var(--regular);
185+
}
186+
.template-prompt__body{
187+
margin: var(--app-margin) 0;
188+
}
189+
.template-prompt__footer{
190+
display: flex;
191+
justify-content: flex-end;
192+
}
193+
.template-prompt__footer button{
194+
margin-left: 16px;
195+
}
196+
.flex-thumbnail-container {
197+
display: flex;
198+
justify-content: center;
199+
align-items: center;
200+
}
201+
202+
.flex-image-button-container {
203+
display: flex;
204+
flex-direction: column;
205+
}
206+
</style>

src/components/sandbox/AppDetails.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
<p>
3838
<span v-if="appProperties.folderAction==true" class="app-install-span">Is a Folder Action</span>
3939
</p>
40+
<p>
41+
<span v-if="appProperties.template.length > 0" class="app-install-span">Multiple instances of App can be installed</span>
42+
</p>
4043
<p v-if="!appHasFileAssociation && appProperties.permissions.length == 0">
4144
<span class="app-install-span">Permissions:</span><span class="app-install-text">None Required</span>
4245
</p>

0 commit comments

Comments
 (0)