UIWindowShare.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import UIWindow from './UIWindow.js'
  2. async function UIWindowShare(items, recipient){
  3. return new Promise(async (resolve) => {
  4. let h = '';
  5. h += `<div style="padding: 30px 40px 20px; border-bottom: 1px solid #ced7e1;">`;
  6. h += `<div class="qr-code-window-close-btn generic-close-window-button" style="margin: 5px;"> &times; </div>`;
  7. // icon
  8. h += `<img src="${items[0].icon}" style="width:70px; height:70px; display: block; margin: 10px auto 0;">`;
  9. // name
  10. h += `<h2 style="font-size: 17px; margin-top:0; text-align:center; margin-bottom: 40px; font-weight: 400; color: #303d49; text-shadow: 1px 1px white;">${items.length > 1 ? `Share ${items.length} items` : `${html_encode(items[0].name)}`}</h2>`;
  11. // form
  12. h += `<form class="window-give-item-access-form">`;
  13. // Error msg
  14. h += `<div class="error"></div>`;
  15. // Username/email
  16. h += `<div style="overflow: hidden;">`;
  17. h += `<label style="font-size: 16px; font-weight: 600;">Share with:</label>`;
  18. h += `<div style="display: flex;">`;
  19. // Username/email
  20. h += `<input placeholder="username" class="access-recipient" value="${html_encode(recipient ?? '')}" style="border-right: none; margin-bottom: 10px; border-top-right-radius: 0; border-bottom-right-radius: 0;" type="text" autocomplete="recipient_email_username" spellcheck="false" autocorrect="off" autocapitalize="off" data-gramm_editor="false"/>`;
  21. // Share
  22. h += `<button class="give-access-btn button button-primary button-normal" style="border-top-left-radius: 0; border-bottom-left-radius: 0;" ${!recipient ? 'disabled' : ''}>Share</button>`
  23. h += `</div>`;
  24. h += `</div>`;
  25. h += `</form>`;
  26. //recipients
  27. h += `<p style="font-size: 14px; margin-bottom: 0px; color: #303d49; text-shadow: 1px 1px white;">People with access</p>`;
  28. h += `<div class="share-recipients">`;
  29. h += `</div>`;
  30. h += `</div>`;
  31. const el_window = await UIWindow({
  32. title: 'Share With…',
  33. icon: null,
  34. uid: null,
  35. is_dir: false,
  36. body_content: h,
  37. has_head: false,
  38. selectable_body: false,
  39. draggable_body: true,
  40. allow_context_menu: false,
  41. is_resizable: false,
  42. is_droppable: false,
  43. init_center: true,
  44. allow_native_ctxmenu: true,
  45. allow_user_select: true,
  46. onAppend: function(this_window){
  47. $(this_window).find(`.access-recipient`).get(0).focus({preventScroll:true});
  48. },
  49. window_class: 'window-give-access',
  50. width: 550,
  51. window_css: {
  52. height: 'initial',
  53. },
  54. body_css: {
  55. width: 'initial',
  56. height: '100%',
  57. 'background-color': 'rgb(245 247 249)',
  58. 'backdrop-filter': 'blur(3px)',
  59. }
  60. })
  61. let contacts = [];
  62. puter.kv.get('contacts').then((kv_contacts) => {
  63. if(kv_contacts){
  64. try{
  65. contacts = JSON.parse(kv_contacts);
  66. $(el_window).find('.access-recipient').autocomplete({
  67. source: contacts
  68. });
  69. }catch(e){
  70. puter.kv.del('contacts');
  71. }
  72. }
  73. })
  74. // /stat
  75. let perms = [];
  76. for(let i=0; i<items.length; i++){
  77. puter.fs.stat({
  78. path: items[i].path,
  79. returnSubdomains: true,
  80. returnPermissions: true,
  81. }).then((fsentry) => {
  82. let recipients = fsentry.shares?.users;
  83. let printed_users = [];
  84. let perm_list = '';
  85. //owner
  86. //check if this user has been printed here before, important for multiple items
  87. if(!printed_users.includes(fsentry.owner.username)){
  88. perm_list += `<div class="item-perm-recipient-card item-prop-perm-entry item-permission-owner" style="margin-bottom:5px; margin-top:5px; background-color: #f2f2f2;">`
  89. if(fsentry.owner.username === window.user.username)
  90. perm_list += `You (${fsentry.owner.email ?? fsentry.owner.username})`;
  91. else
  92. perm_list += fsentry.owner.email ?? fsentry.owner.username;
  93. perm_list += `<div style="float:right;"><span class="permission-owner-badge">owner</span></div>`;
  94. perm_list += `</div>`;
  95. // add this user to the list of printed users
  96. printed_users.push(fsentry.owner.username);
  97. }
  98. if(recipients.length > 0){
  99. recipients.forEach((recipient) => {
  100. // others with access
  101. if(recipients.length > 0){
  102. recipients.forEach(perm => {
  103. //check if this user has been printed here before, important for multiple items
  104. if(!printed_users.includes(perm.user.username)){
  105. perm_list += `<div data-permission="${perm.permission}" class="item-perm-recipient-card item-prop-perm-entry" data-perm-uid="${perm.user.uid}" style="margin-bottom:5px; margin-top:5px;">`
  106. perm_list += `${perm.user.email ?? perm.user.username}`;
  107. perm_list += `<div style="float:right;"><span class="remove-permission-link remove-permission-icon" data-recipient-username="${perm.user.username}" data-permission="${perm.permission}">✕</span></div>`;
  108. perm_list += `</div>`;
  109. // add this user to the list of printed users
  110. printed_users.push(perm.user.username);
  111. }
  112. });
  113. }
  114. });
  115. }
  116. $(el_window).find('.share-recipients').append(`${perm_list}`);
  117. }).
  118. catch((err) => {
  119. // console.error(err);
  120. })
  121. }
  122. $(el_window).find('.give-access-btn').on('click', async function(e){
  123. e.preventDefault();
  124. e.stopPropagation();
  125. $(el_window).find('.error').hide();
  126. let recipient_email, recipient_username;
  127. let recipient_id = $(el_window).find('.access-recipient').val();
  128. // todo do some basic validation client-side
  129. if(recipient_id === '' || recipient_id === null || recipient_id === undefined)
  130. return;
  131. if(is_email(recipient_id))
  132. recipient_email = recipient_id;
  133. else
  134. recipient_username = recipient_id;
  135. // can't share with self
  136. if(recipient_username === window.user.username){
  137. $(el_window).find('.error').html('You can\'t share with yourself');
  138. $(el_window).find('.error').fadeIn();
  139. return;
  140. }
  141. else if(recipient_email && recipient_email === window.user.email){
  142. $(el_window).find('.error').html('You can\'t share with yourself');
  143. $(el_window).find('.error').fadeIn();
  144. return;
  145. }
  146. // disable 'Give Access' button
  147. $(el_window).find('.give-access-btn').prop('disabled', true);
  148. let cancelled_due_to_error = false;
  149. let share_result;
  150. let access_level = 'write';
  151. $.ajax({
  152. url: puter.APIOrigin + "/share",
  153. type: "POST",
  154. headers: {
  155. "Content-Type": "application/json",
  156. "Authorization": "Bearer " + puter.authToken
  157. },
  158. data: JSON.stringify({
  159. recipients:[
  160. recipient_username
  161. ],
  162. paths: [
  163. {
  164. path: items[0].path,
  165. access: access_level,
  166. }
  167. ]
  168. }),
  169. success: function(response) {
  170. // show success message
  171. $(el_window).find('.access-recipient-print').html(recipient_id);
  172. let perm_id = `fs:${items[0].uid}:${access_level}`;
  173. // append recipient to list
  174. let perm_list = '';
  175. perm_list += `<div data-permission="${perm_id}" class="item-perm-recipient-card item-prop-perm-entry" style="margin-bottom:5px; margin-top:5px;">`
  176. perm_list += `${recipient_username}`;
  177. perm_list += `<div style="float:right;"><span class="remove-permission-link remove-permission-icon" data-recipient-username="${recipient_username}" data-permission="${perm_id}">✕</span></div>`;
  178. perm_list += `</div>`;
  179. // append recipient to list
  180. $(el_window).find('.share-recipients').append(`${perm_list}`);
  181. // add to contacts
  182. if(!contacts.includes(recipient_username)){
  183. contacts.push(recipient_username);
  184. puter.kv.set('contacts', JSON.stringify(contacts));
  185. }
  186. },
  187. error: function(err) {
  188. // at this point 'username_not_found' and 'shared_with_self' are the only
  189. // errors that need to stop the loop
  190. if(err.responseJSON.code === "user_does_not_exist" || err.responseJSON.code === 'shared_with_self'){
  191. $(el_window).find('.error').html(err.responseJSON.message);
  192. $(el_window).find('.error').fadeIn();
  193. cancelled_due_to_error = true;
  194. }
  195. // re-enable share button
  196. $(el_window).find('.give-access-btn').prop('disabled', false);
  197. }
  198. });
  199. // finished
  200. if(!cancelled_due_to_error){
  201. $(el_window).find(`.access-recipient`).val('');
  202. }
  203. // re-enable share button
  204. $(el_window).find('.give-access-btn').prop('disabled', false);
  205. return false;
  206. })
  207. $(el_window).find('.access-recipient').on('input keypress keyup keydown paste', function(){
  208. if($(this).val() === ''){
  209. $(el_window).find('.give-access-btn').prop('disabled', true);
  210. }
  211. else{
  212. $(el_window).find('.give-access-btn').prop('disabled', false);
  213. }
  214. })
  215. })
  216. }
  217. $(document).on('click', '.remove-permission-link', async function(){
  218. let recipient_username = $(this).attr('data-recipient-username');
  219. let permission = $(this).attr('data-permission');
  220. fetch(puter.APIOrigin + "/auth/revoke-user-user", {
  221. "headers": {
  222. "Content-Type": "application/json",
  223. "Authorization": `Bearer ${puter.authToken}`,
  224. },
  225. "body": JSON.stringify({
  226. permission: permission,
  227. target_username: recipient_username
  228. }),
  229. "method": "POST"
  230. }).then((response) => {
  231. $('.item-perm-recipient-card[data-permission="'+permission+'"]').remove();
  232. }).catch((err) => {
  233. console.error(err);
  234. })
  235. })
  236. export default UIWindowShare