UITaskbar.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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. // init clock visibility
  42. window.change_clock_visible();
  43. //---------------------------------------------
  44. // add `Start` to taskbar
  45. //---------------------------------------------
  46. UITaskbarItem({
  47. icon: window.icons['start.svg'],
  48. name: i18n('start'),
  49. sortable: false,
  50. keep_in_taskbar: true,
  51. disable_context_menu: true,
  52. onClick: async function(item){
  53. // skip if popover already open
  54. if($(item).hasClass('has-open-popover'))
  55. return;
  56. // show popover
  57. let popover = UIPopover({
  58. content: `<div class="launch-popover hide-scrollbar"><span class="close-launch-popover">✕</span></div>`,
  59. snapToElement: item,
  60. parent_element: item,
  61. width: 500,
  62. height: 500,
  63. center_horizontally: true,
  64. });
  65. // In the rare case that launch_apps is not populated yet, get it from the server
  66. // then populate the popover
  67. if(!launch_apps || !launch_apps.recent || launch_apps.recent.length === 0){
  68. // get launch apps
  69. launch_apps = await $.ajax({
  70. url: api_origin + "/get-launch-apps",
  71. type: 'GET',
  72. async: true,
  73. contentType: "application/json",
  74. headers: {
  75. "Authorization": "Bearer "+auth_token
  76. },
  77. });
  78. }
  79. let apps_str = '';
  80. apps_str += `<div style="margin-bottom: 10px; padding: 5px; position: relative;">`
  81. apps_str += `<input style="background-image:url(${window.icons['magnifier-outline.svg']});" class="launch-search">`;
  82. apps_str += `<img class="launch-search-clear" src="${window.icons['close.svg']}">`;
  83. apps_str += `</div>`;
  84. // -------------------------------------------
  85. // Recent apps
  86. // -------------------------------------------
  87. if(launch_apps.recent.length > 0){
  88. // heading
  89. apps_str += `<h1 class="start-section-heading start-section-heading-recent">${i18n('recent')}</h1>`;
  90. // apps
  91. apps_str += `<div class="launch-apps-recent">`;
  92. for (let index = 0; index < window.launch_recent_apps_count && index < launch_apps.recent.length; index++) {
  93. const app_info = launch_apps.recent[index];
  94. apps_str += `<div title="${html_encode(app_info.title)}" data-name="${html_encode(app_info.name)}" class="start-app-card">`;
  95. 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)}">`;
  96. apps_str += `<img class="start-app-icon" src="${html_encode(app_info.icon ? app_info.icon : window.icons['app.svg'])}">`;
  97. apps_str += `<span class="start-app-title">${html_encode(app_info.title)}</span>`;
  98. apps_str += `</div>`;
  99. apps_str += `</div>`;
  100. }
  101. apps_str += `</div>`;
  102. }
  103. // -------------------------------------------
  104. // Reccomended apps
  105. // -------------------------------------------
  106. if(launch_apps.recommended.length > 0){
  107. // heading
  108. apps_str += `<h1 class="start-section-heading start-section-heading-recommended" style="${launch_apps.recent.length > 0 ? 'padding-top: 30px;' : ''}">Recommended</h1>`;
  109. // apps
  110. apps_str += `<div class="launch-apps-recommended">`;
  111. for (let index = 0; index < launch_apps.recommended.length; index++) {
  112. const app_info = launch_apps.recommended[index];
  113. apps_str += `<div title="${html_encode(app_info.title)}" data-name="${html_encode(app_info.name)}" class="start-app-card">`;
  114. 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)}">`;
  115. apps_str += `<img class="start-app-icon" src="${html_encode(app_info.icon ? app_info.icon : window.icons['app.svg'])}">`;
  116. apps_str += `<span class="start-app-title">${html_encode(app_info.title)}</span>`;
  117. apps_str += `</div>`;
  118. apps_str += `</div>`;
  119. }
  120. apps_str += `</div>`;
  121. }
  122. // add apps to popover
  123. $(popover).find('.launch-popover').append(apps_str);
  124. // focus on search input only if not on mobile
  125. if(!isMobile.phone)
  126. $(popover).find('.launch-search').focus();
  127. // make apps draggable
  128. $(popover).find('.start-app').draggable({
  129. appendTo: "body",
  130. revert: "invalid",
  131. connectToSortable: ".taskbar",
  132. //containment: "document",
  133. zIndex: parseInt($(popover).css('z-index')) + 1,
  134. scroll: false,
  135. distance: 5,
  136. revertDuration: 100,
  137. helper: 'clone',
  138. cursorAt: { left: 18, top: 20 },
  139. start: function(event, ui){
  140. },
  141. drag: function(event, ui){
  142. },
  143. stop: function(){
  144. }
  145. });
  146. }
  147. });
  148. //---------------------------------------------
  149. // add `Explorer` to the taskbar
  150. //---------------------------------------------
  151. UITaskbarItem({
  152. icon: window.icons['folders.svg'],
  153. app: 'explorer',
  154. name: 'Explorer',
  155. sortable: false,
  156. keep_in_taskbar: true,
  157. lock_keep_in_taskbar: true,
  158. onClick: function(){
  159. let open_window_count = parseInt($(`.taskbar-item[data-app="explorer"]`).attr('data-open-windows'));
  160. if(open_window_count === 0){
  161. launch_app({ name: 'explorer', path: window.home_path});
  162. }else{
  163. return false;
  164. }
  165. }
  166. })
  167. //---------------------------------------------
  168. // Add other useful apps to the taskbar
  169. //---------------------------------------------
  170. if(window.user.taskbar_items && window.user.taskbar_items.length > 0){
  171. for (let index = 0; index < window.user.taskbar_items.length; index++) {
  172. const app_info = window.user.taskbar_items[index];
  173. // add taskbar item for each app
  174. UITaskbarItem({
  175. icon: app_info.icon,
  176. app: app_info.name,
  177. name: app_info.title,
  178. keep_in_taskbar: true,
  179. onClick: function(){
  180. let open_window_count = parseInt($(`.taskbar-item[data-app="${app_info.name}"]`).attr('data-open-windows'));
  181. if(open_window_count === 0){
  182. launch_app({
  183. name: app_info.name,
  184. })
  185. }else{
  186. return false;
  187. }
  188. }
  189. });
  190. }
  191. }
  192. //---------------------------------------------
  193. // add `Trash` to the taskbar
  194. //---------------------------------------------
  195. const trash = await puter.fs.stat(trash_path);
  196. if(window.socket){
  197. window.socket.emit('trash.is_empty', {is_empty: trash.is_empty});
  198. }
  199. UITaskbarItem({
  200. icon: trash.is_empty ? window.icons['trash.svg'] : window.icons['trash-full.svg'],
  201. app: 'trash',
  202. name: `${i18n('trash')}`,
  203. sortable: false,
  204. keep_in_taskbar: true,
  205. lock_keep_in_taskbar: true,
  206. onClick: function(){
  207. let open_windows = $(`.window[data-path="${html_encode(trash_path)}"]`);
  208. if(open_windows.length === 0){
  209. launch_app({ name: 'explorer', path: window.trash_path});
  210. }else{
  211. open_windows.focusWindow();
  212. }
  213. },
  214. onItemsDrop: function(items){
  215. move_items(items, trash_path);
  216. }
  217. })
  218. make_taskbar_sortable();
  219. }
  220. window.make_taskbar_sortable = function(){
  221. //-------------------------------------------
  222. // Taskbar is sortable
  223. //-------------------------------------------
  224. $('.taskbar').sortable({
  225. axis: "x",
  226. items: '.taskbar-item-sortable:not(.has-open-contextmenu)',
  227. cancel: '.has-open-contextmenu',
  228. placeholder: "taskbar-item-sortable-placeholder",
  229. helper : 'clone',
  230. distance: 5,
  231. revert: 10,
  232. receive: function(event, ui){
  233. if(!$(ui.item).hasClass('taskbar-item')){
  234. // if app is already in taskbar, cancel
  235. if($(`.taskbar-item[data-app="${$(ui.item).attr('data-app-name')}"]`).length !== 0){
  236. $(this).sortable('cancel');
  237. $('.taskbar .start-app').remove();
  238. return;
  239. }else{
  240. }
  241. }
  242. },
  243. update: function(event, ui){
  244. if(!$(ui.item).hasClass('taskbar-item')){
  245. // if app is already in taskbar, cancel
  246. if($(`.taskbar-item[data-app="${$(ui.item).attr('data-app-name')}"]`).length !== 0){
  247. $(this).sortable('cancel');
  248. $('.taskbar .start-app').remove();
  249. return;
  250. }
  251. let item = UITaskbarItem({
  252. icon: $(ui.item).attr('data-app-icon'),
  253. app: $(ui.item).attr('data-app-name'),
  254. name: $(ui.item).attr('data-app-title'),
  255. append_to_taskbar: false,
  256. keep_in_taskbar: true,
  257. onClick: function(){
  258. let open_window_count = parseInt($(`.taskbar-item[data-app="${$(ui.item).attr('data-app-name')}"]`).attr('data-open-windows'));
  259. if(open_window_count === 0){
  260. launch_app({
  261. name: $(ui.item).attr('data-app-name'),
  262. })
  263. }else{
  264. return false;
  265. }
  266. }
  267. });
  268. let el = ($(item).detach())
  269. $(el).insertAfter(ui.item);
  270. // $(ui.item).insertBefore(`<h1>Hello!</h1>`);
  271. $(el).show();
  272. $(ui.item).removeItems();
  273. update_taskbar();
  274. }
  275. // only proceed to update DB if the item sorted was a pinned item otherwise no point in updating the taskbar in DB
  276. else if($(ui.item).attr('data-keep-in-taskbar') === 'true'){
  277. update_taskbar();
  278. }
  279. },
  280. });
  281. }
  282. export default UITaskbar;