UITaskbar.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /**
  2. * Copyright (C) 2024 Puter Technologies Inc.
  3. *
  4. * This file is part of Puter.
  5. *
  6. * Puter is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published
  8. * by the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. import UITaskbarItem from './UITaskbarItem.js'
  20. import UIPopover from './UIPopover.js'
  21. async function UITaskbar(options){
  22. global_element_id++;
  23. options = options ?? {};
  24. options.content = options.content ?? '';
  25. // get launch apps
  26. $.ajax({
  27. url: api_origin + "/get-launch-apps",
  28. type: 'GET',
  29. async: true,
  30. contentType: "application/json",
  31. headers: {
  32. "Authorization": "Bearer "+auth_token
  33. },
  34. success: function (apps){
  35. window.launch_apps = apps;
  36. }
  37. });
  38. let h = '';
  39. h += `<div id="ui-taskbar_${global_element_id}" class="taskbar" style="height:${window.taskbar_height}px;"><span id='clock'></span></div>`;
  40. $('.desktop').append(h);
  41. //---------------------------------------------
  42. // add `Start` to taskbar
  43. //---------------------------------------------
  44. UITaskbarItem({
  45. icon: window.icons['start.svg'],
  46. name: i18n('start'),
  47. sortable: false,
  48. keep_in_taskbar: true,
  49. disable_context_menu: true,
  50. onClick: async function(item){
  51. // skip if popover already open
  52. if($(item).hasClass('has-open-popover'))
  53. return;
  54. // show popover
  55. let popover = UIPopover({
  56. content: `<div class="launch-popover hide-scrollbar"><span class="close-launch-popover">✕</span></div>`,
  57. snapToElement: item,
  58. parent_element: item,
  59. width: 500,
  60. height: 500,
  61. center_horizontally: true,
  62. });
  63. // In the rare case that launch_apps is not populated yet, get it from the server
  64. // then populate the popover
  65. if(!launch_apps || !launch_apps.recent || launch_apps.recent.length === 0){
  66. // get launch apps
  67. launch_apps = await $.ajax({
  68. url: api_origin + "/get-launch-apps",
  69. type: 'GET',
  70. async: true,
  71. contentType: "application/json",
  72. headers: {
  73. "Authorization": "Bearer "+auth_token
  74. },
  75. });
  76. }
  77. let apps_str = '';
  78. apps_str += `<div style="margin-bottom: 10px; padding: 5px; position: relative;">`
  79. apps_str += `<input style="background-image:url(${window.icons['magnifier-outline.svg']});" class="launch-search">`;
  80. apps_str += `<img class="launch-search-clear" src="${window.icons['close.svg']}">`;
  81. apps_str += `</div>`;
  82. // -------------------------------------------
  83. // Recent apps
  84. // -------------------------------------------
  85. if(launch_apps.recent.length > 0){
  86. // heading
  87. apps_str += `<h1 class="start-section-heading start-section-heading-recent">${i18n('recent')}</h1>`;
  88. // apps
  89. apps_str += `<div class="launch-apps-recent">`;
  90. for (let index = 0; index < window.launch_recent_apps_count && index < launch_apps.recent.length; index++) {
  91. const app_info = launch_apps.recent[index];
  92. apps_str += `<div title="${html_encode(app_info.title)}" data-name="${html_encode(app_info.name)}" class="start-app-card">`;
  93. apps_str += `<div class="start-app" data-app-name="${html_encode(app_info.name)}" data-app-uuid="${html_encode(app_info.uuid)}" data-app-icon="${html_encode(app_info.icon)}" data-app-title="${html_encode(app_info.title)}">`;
  94. apps_str += `<img class="start-app-icon" src="${html_encode(app_info.icon ? app_info.icon : window.icons['app.svg'])}">`;
  95. apps_str += `<span class="start-app-title">${html_encode(app_info.title)}</span>`;
  96. apps_str += `</div>`;
  97. apps_str += `</div>`;
  98. }
  99. apps_str += `</div>`;
  100. }
  101. // -------------------------------------------
  102. // Reccomended apps
  103. // -------------------------------------------
  104. if(launch_apps.recommended.length > 0){
  105. // heading
  106. apps_str += `<h1 class="start-section-heading start-section-heading-recommended" style="${launch_apps.recent.length > 0 ? 'padding-top: 30px;' : ''}">Recommended</h1>`;
  107. // apps
  108. apps_str += `<div class="launch-apps-recommended">`;
  109. for (let index = 0; index < launch_apps.recommended.length; index++) {
  110. const app_info = launch_apps.recommended[index];
  111. apps_str += `<div title="${html_encode(app_info.title)}" data-name="${html_encode(app_info.name)}" class="start-app-card">`;
  112. apps_str += `<div class="start-app" data-app-name="${html_encode(app_info.name)}" data-app-uuid="${html_encode(app_info.uuid)}" data-app-icon="${html_encode(app_info.icon)}" data-app-title="${html_encode(app_info.title)}">`;
  113. apps_str += `<img class="start-app-icon" src="${html_encode(app_info.icon ? app_info.icon : window.icons['app.svg'])}">`;
  114. apps_str += `<span class="start-app-title">${html_encode(app_info.title)}</span>`;
  115. apps_str += `</div>`;
  116. apps_str += `</div>`;
  117. }
  118. apps_str += `</div>`;
  119. }
  120. // add apps to popover
  121. $(popover).find('.launch-popover').append(apps_str);
  122. // focus on search input only if not on mobile
  123. if(!isMobile.phone)
  124. $(popover).find('.launch-search').focus();
  125. // make apps draggable
  126. $(popover).find('.start-app').draggable({
  127. appendTo: "body",
  128. revert: "invalid",
  129. connectToSortable: ".taskbar",
  130. //containment: "document",
  131. zIndex: parseInt($(popover).css('z-index')) + 1,
  132. scroll: false,
  133. distance: 5,
  134. revertDuration: 100,
  135. helper: 'clone',
  136. cursorAt: { left: 18, top: 20 },
  137. start: function(event, ui){
  138. },
  139. drag: function(event, ui){
  140. },
  141. stop: function(){
  142. }
  143. });
  144. }
  145. });
  146. //---------------------------------------------
  147. // add `Explorer` to the taskbar
  148. //---------------------------------------------
  149. UITaskbarItem({
  150. icon: window.icons['folders.svg'],
  151. app: 'explorer',
  152. name: 'Explorer',
  153. sortable: false,
  154. keep_in_taskbar: true,
  155. lock_keep_in_taskbar: true,
  156. onClick: function(){
  157. let open_window_count = parseInt($(`.taskbar-item[data-app="explorer"]`).attr('data-open-windows'));
  158. if(open_window_count === 0){
  159. launch_app({ name: 'explorer', path: window.home_path});
  160. }else{
  161. return false;
  162. }
  163. }
  164. })
  165. //---------------------------------------------
  166. // Add other useful apps to the taskbar
  167. //---------------------------------------------
  168. if(window.user.taskbar_items && window.user.taskbar_items.length > 0){
  169. for (let index = 0; index < window.user.taskbar_items.length; index++) {
  170. const app_info = window.user.taskbar_items[index];
  171. // add taskbar item for each app
  172. UITaskbarItem({
  173. icon: app_info.icon,
  174. app: app_info.name,
  175. name: app_info.title,
  176. keep_in_taskbar: true,
  177. onClick: function(){
  178. let open_window_count = parseInt($(`.taskbar-item[data-app="${app_info.name}"]`).attr('data-open-windows'));
  179. if(open_window_count === 0){
  180. launch_app({
  181. name: app_info.name,
  182. })
  183. }else{
  184. return false;
  185. }
  186. }
  187. });
  188. }
  189. }
  190. //---------------------------------------------
  191. // add `Trash` to the taskbar
  192. //---------------------------------------------
  193. const trash = await puter.fs.stat(trash_path);
  194. if(window.socket){
  195. window.socket.emit('trash.is_empty', {is_empty: trash.is_empty});
  196. }
  197. UITaskbarItem({
  198. icon: trash.is_empty ? window.icons['trash.svg'] : window.icons['trash-full.svg'],
  199. app: 'trash',
  200. name: `${i18n('trash')}`,
  201. sortable: false,
  202. keep_in_taskbar: true,
  203. lock_keep_in_taskbar: true,
  204. onClick: function(){
  205. let open_windows = $(`.window[data-path="${html_encode(trash_path)}"]`);
  206. if(open_windows.length === 0){
  207. launch_app({ name: 'explorer', path: window.trash_path});
  208. }else{
  209. open_windows.focusWindow();
  210. }
  211. },
  212. onItemsDrop: function(items){
  213. move_items(items, trash_path);
  214. }
  215. })
  216. make_taskbar_sortable();
  217. }
  218. window.make_taskbar_sortable = function(){
  219. //-------------------------------------------
  220. // Taskbar is sortable
  221. //-------------------------------------------
  222. $('.taskbar').sortable({
  223. axis: "x",
  224. items: '.taskbar-item-sortable:not(.has-open-contextmenu)',
  225. cancel: '.has-open-contextmenu',
  226. placeholder: "taskbar-item-sortable-placeholder",
  227. helper : 'clone',
  228. distance: 5,
  229. revert: 10,
  230. receive: function(event, ui){
  231. if(!$(ui.item).hasClass('taskbar-item')){
  232. // if app is already in taskbar, cancel
  233. if($(`.taskbar-item[data-app="${$(ui.item).attr('data-app-name')}"]`).length !== 0){
  234. $(this).sortable('cancel');
  235. $('.taskbar .start-app').remove();
  236. return;
  237. }else{
  238. }
  239. }
  240. },
  241. update: function(event, ui){
  242. if(!$(ui.item).hasClass('taskbar-item')){
  243. // if app is already in taskbar, cancel
  244. if($(`.taskbar-item[data-app="${$(ui.item).attr('data-app-name')}"]`).length !== 0){
  245. $(this).sortable('cancel');
  246. $('.taskbar .start-app').remove();
  247. return;
  248. }
  249. let item = UITaskbarItem({
  250. icon: $(ui.item).attr('data-app-icon'),
  251. app: $(ui.item).attr('data-app-name'),
  252. name: $(ui.item).attr('data-app-title'),
  253. append_to_taskbar: false,
  254. keep_in_taskbar: true,
  255. onClick: function(){
  256. let open_window_count = parseInt($(`.taskbar-item[data-app="${$(ui.item).attr('data-app-name')}"]`).attr('data-open-windows'));
  257. if(open_window_count === 0){
  258. launch_app({
  259. name: $(ui.item).attr('data-app-name'),
  260. })
  261. }else{
  262. return false;
  263. }
  264. }
  265. });
  266. let el = ($(item).detach())
  267. $(el).insertAfter(ui.item);
  268. // $(ui.item).insertBefore(`<h1>Hello!</h1>`);
  269. $(el).show();
  270. $(ui.item).removeItems();
  271. update_taskbar();
  272. }
  273. // only proceed to update DB if the item sorted was a pinned item otherwise no point in updating the taskbar in DB
  274. else if($(ui.item).attr('data-keep-in-taskbar') === 'true'){
  275. update_taskbar();
  276. }
  277. },
  278. });
  279. }
  280. export default UITaskbar;