@trap_stevo/filetide 0.0.39 → 0.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,11 +5,13 @@ const {
5
5
  } = require("@trap_stevo/iotide-client");
6
6
  const chalk = require("chalk");
7
7
  const path = require("path");
8
+ const fs = require("fs");
8
9
  const {
9
10
  FileNetUtilityManager,
10
11
  significantMessageColors,
11
12
  errorMessageColors
12
13
  } = require("./HUDManagers/FileNetUtilityManager");
14
+ const FileTransferRecoveryManager = require("./HUDManagers/FileTransferRecoveryManager");
13
15
  const {
14
16
  FileMessagerConfigManager
15
17
  } = require("./HUDManagers/FileMessagerConfigManager");
@@ -19,7 +21,6 @@ const {
19
21
  const {
20
22
  FileUtilityManager
21
23
  } = require("./HUDManagers/FileUtilityManager");
22
- const ConsoleProgressBarManager = require("./HUDComponents/ConsoleProgressBarManager");
23
24
  class FileMessagerClient {
24
25
  constructor(options = {}, transportOptions = {
25
26
  parallelChunks: 3,
@@ -27,25 +28,27 @@ class FileMessagerClient {
27
28
  }) {
28
29
  this.clientOptions = FileMessagerConfigManager.getClientOptions(options);
29
30
  this.transporter = new FileTransferManager(transportOptions);
30
- this.progressBarManager = new ConsoleProgressBarManager([], {
31
- completionMessageColor: chalk.blue.bold
32
- });
33
31
  this.activeTransferTypes = new Map();
34
32
  this.clientTide = new HUDIoTide();
35
33
  this.clients = new Map();
36
34
  }
37
- createClientInstance(clientID, clientOptions, options = {}) {
35
+ createClientInstance(clientID, clientOptions, options = {}, headers = {}, onConnect = null, onDisconnect = null, authURL = "", authHeaders = {}, useAuthentication = false, queryAuthToken = false) {
38
36
  this.clientTide.createIO(clientID, clientOptions.url, {
39
37
  transports: ["websocket"],
40
38
  ...options
39
+ }, headers, authURL, authHeaders, useAuthentication, queryAuthToken, onConnect, () => {
40
+ FileTransferRecoveryManager.flushToDisk();
41
+ if (onDisconnect) {
42
+ onDisconnect();
43
+ }
41
44
  });
42
45
  FileNetUtilityManager.outputGradient(`\n[FileTide ~ File Messager] ~ Created client with ID ${clientID} and socket ${clientID}!`, significantMessageColors);
43
46
  return clientID;
44
47
  }
45
- joinRoom(userID, roomName, options = {}) {
48
+ joinRoom(userID, roomName, options = {}, headers = {}, onConnect = null, onDisconnect = null, authURL = "", authHeaders = {}, useAuthentication = false, queryAuthToken = false) {
46
49
  const socketName = this.createClientInstance(userID, {
47
50
  url: this.clientOptions.url
48
- }, options);
51
+ }, options, headers, onConnect, onDisconnect, authURL, authHeaders, useAuthentication, queryAuthToken);
49
52
  this.clientTide.joinChannel(socketName, roomName, userID, () => {
50
53
  FileNetUtilityManager.outputGradient(`\n[FileTide ~ File Messager] ~ Client ~ ${userID} joined room ${roomName} successfully!`, significantMessageColors);
51
54
  this.clients.set(userID, {
@@ -111,8 +114,11 @@ class FileMessagerClient {
111
114
  * @param {Array} filesData - Array of file data objects from the directory.
112
115
  * @param {string} baseDirectory - The base directory path that contains the files.
113
116
  * @param {string} destinationPath - The path to which the files should be sent.
117
+ * @param {Number} tideSize - The tide size in sending files (500)
118
+ * @param {Number} minTideSize - The minimum tide size in sending files (10)
119
+ * @param {Number} maxTideSize - The maximum tide size in sending files (2000)
114
120
  */
115
- async sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath) {
121
+ async sendDirectoryFiles(clientID, recipientId, filesData, baseDirectory, destinationPath, tideSize = 500, minTideSize = 10, maxTideSize = 2000) {
116
122
  try {
117
123
  const baseDirectoryName = path.basename(baseDirectory);
118
124
  const adjustedDestinationPath = FileUtilityManager.normalizePath(path.join(destinationPath, baseDirectoryName));
@@ -131,6 +137,9 @@ class FileMessagerClient {
131
137
  const sendFilePromises = filesData.map(async (fileInfo, index) => {
132
138
  const relativeFilePath = path.dirname(path.relative(baseDirectory, fileInfo.filePath));
133
139
  const fileDestinationPath = FileUtilityManager.normalizePath(path.join(adjustedDestinationPath, relativeFilePath));
140
+ if (fileInfo.largeFile) {
141
+ return await this.sendDirectoryLargeFile(clientID, recipientId, fileInfo.fileName, fileInfo.filePath, fileDestinationPath, fileInfo.fileSize, index, tideSize, minTideSize, maxTideSize);
142
+ }
134
143
  return await this.sendDirectoryFile(clientID, recipientId, fileInfo.fileName, fileInfo.fileData, fileDestinationPath, fileInfo.size, adjustedDestinationPath, index);
135
144
  });
136
145
  Promise.all(sendFilePromises).then(() => console.log("\nAll files in directory transferred successfully!")).catch(error => console.error("Did not transfer directory: ", error));
@@ -138,29 +147,28 @@ class FileMessagerClient {
138
147
  FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused directory transfer: ${error}`, errorMessageColors);
139
148
  }
140
149
  }
150
+ async sendDirectoryLargeFile(clientID, recipientId, fileName, filePath = process.cwd(), destinationPath = null, fileSize, index = 0, tideSize = 500, minTideSize = 10, maxTideSize = 2000) {
151
+ let initiatingTransfer = false;
152
+ if (index === 0) {
153
+ initiatingTransfer = true;
154
+ }
155
+ return await this.sendLargeFile(clientID, recipientId, fileName, filePath, destinationPath, fileSize, tideSize, minTideSize, maxTideSize, initiatingTransfer, true);
156
+ }
141
157
  sendDirectoryFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize, directoryPath = null, index = 0) {
142
158
  if (!file) {
143
159
  return;
144
160
  }
145
161
  if (index === 0) {
146
- this.progressBarManager.completionMessage = `Successfully sent to ${recipientId}!`;
147
- this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
148
- this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
149
162
  this.clientTide.emitEvent(clientID, "transfer-start", {
150
163
  fileName,
151
164
  senderID: clientID,
152
165
  recipientID: recipientId,
153
- path: directoryPath || filePath
166
+ path: directoryPath || filePath,
167
+ fileSize
154
168
  });
155
169
  }
156
170
  this.transporter.sendFile(file, {
157
171
  onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks) => {
158
- if (chunkIndex === 0) {
159
- this.progressBarManager.addTask({
160
- name: fileName,
161
- size: fileSize
162
- });
163
- }
164
172
  return new Promise((resolve, reject) => {
165
173
  this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
166
174
  senderID: clientID,
@@ -187,19 +195,124 @@ class FileMessagerClient {
187
195
  resolve();
188
196
  });
189
197
  },
190
- onProgress: (progress, chunkSize) => {
191
- this.progressBarManager.updateTaskProgress(fileName, chunkSize);
192
- },
193
198
  originDetails: {
194
199
  recipientID: recipientId,
195
200
  senderID: clientID
196
201
  },
202
+ recipientID: recipientId,
197
203
  fileDetails: {
204
+ destinationPath: filePath,
198
205
  name: fileName,
199
206
  path: filePath,
200
207
  size: fileSize
201
208
  }
202
- }).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
209
+ }, this.clientTide).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
210
+ }
211
+ async sendLargeFile(clientID, recipientId, fileName, filePath = process.cwd(), destination = process.cwd(), fileSize, tideSize = 500, minTideSize = 10, maxTideSize = 2000, initiatingTransfer = true, inDirectory = false) {
212
+ const destinationPath = FileUtilityManager.normalizePath(path.join(destination, fileName));
213
+ FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Requesting to transfer large file ~ ${destinationPath} to ${recipientId}!`, significantMessageColors);
214
+ const incomingType = this.activeTransferTypes.get(filePath);
215
+ if (!inDirectory && initiatingTransfer) {
216
+ const transferAllowed = incomingType && incomingType === "requestedTransfer" ? true : await this.allowedTransfer(clientID, recipientId, destinationPath, fileSize, "send");
217
+ if (incomingType) {
218
+ this.activeTransferTypes.delete(filePath);
219
+ }
220
+ if (!transferAllowed) {
221
+ return;
222
+ }
223
+ this.clientTide.emitEvent(clientID, "transfer-start", {
224
+ fileName,
225
+ senderID: clientID,
226
+ recipientID: recipientId,
227
+ path: destination,
228
+ fileSize
229
+ });
230
+ } else if (inDirectory && !initiatingTransfer) {
231
+ if (incomingType) {
232
+ this.activeTransferTypes.delete(filePath);
233
+ }
234
+ }
235
+ const fileHandle = await fs.promises.open(filePath, "r");
236
+ const fileDetails = {
237
+ destinationPath,
238
+ name: fileName,
239
+ path: filePath,
240
+ size: fileSize
241
+ };
242
+ try {
243
+ let lastSendTime = Date.now();
244
+ const maxBatchSize = maxTideSize;
245
+ const minBatchSize = minTideSize;
246
+ let batchSize = tideSize > maxBatchSize || tideSize > minBatchSize ? tideSize : tideSize <= minBatchSize ? minBatchSize : 10;
247
+ const chunkBatch = [];
248
+ await this.transporter.sendLargeFile(fileHandle, {
249
+ recipientID: recipientId,
250
+ originDetails: {
251
+ recipientID: recipientId,
252
+ senderID: clientID
253
+ },
254
+ fileDetails,
255
+ onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks, completedChunks) => {
256
+ return new Promise(resolve => {
257
+ chunkBatch.push({
258
+ senderID: clientID,
259
+ recipientId,
260
+ fileName,
261
+ fileChunk: chunkData,
262
+ completedChunks,
263
+ totalChunks,
264
+ chunkIndex,
265
+ chunkSize,
266
+ filePath: destination,
267
+ fileSize
268
+ });
269
+ if (chunkBatch.length >= batchSize) {
270
+ const now = Date.now();
271
+ const rtt = now - lastSendTime;
272
+ lastSendTime = now;
273
+ this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
274
+ chunkBatch
275
+ });
276
+ chunkBatch.length = 0;
277
+ if (rtt >= 200 && batchSize < maxBatchSize) {
278
+ batchSize = Math.min(batchSize + 50, maxBatchSize);
279
+ } else if (rtt < 200 && batchSize > minBatchSize) {
280
+ batchSize = Math.max(batchSize - 50, minBatchSize);
281
+ }
282
+ }
283
+ resolve();
284
+ });
285
+ },
286
+ onComplete: transferId => {
287
+ return new Promise(resolve => {
288
+ if (chunkBatch.length > 0) {
289
+ this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
290
+ chunkBatch
291
+ });
292
+ chunkBatch.length = 0;
293
+ }
294
+ this.clientTide.emitEvent(clientID, "transfer-complete", {
295
+ senderID: clientID,
296
+ recipientId,
297
+ fileName,
298
+ filePath: destination
299
+ });
300
+ resolve();
301
+ });
302
+ },
303
+ onTiding: () => {
304
+ this.clientTide.emitEvent(clientID, "client-tiding", {
305
+ senderID: clientID,
306
+ message: "Transfer ongoing..."
307
+ });
308
+ }
309
+ }, this.clientTide);
310
+ FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Large file ~ ${fileName} transfer complete!`, significantMessageColors);
311
+ } catch (error) {
312
+ FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Error transferring large file: ${error.message}`, errorMessageColors);
313
+ } finally {
314
+ await fileHandle.close();
315
+ }
203
316
  }
204
317
  async sendFile(clientID, recipientId, fileName, file, filePath = process.cwd(), fileSize) {
205
318
  if (!file) {
@@ -215,29 +328,22 @@ class FileMessagerClient {
215
328
  if (!transferAllowed) {
216
329
  return;
217
330
  }
218
- this.progressBarManager.completionMessage = `Successfully sent to ${recipientId}!`;
219
- this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
220
- this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
221
331
  this.clientTide.emitEvent(clientID, "transfer-start", {
222
332
  fileName,
223
333
  senderID: clientID,
224
334
  recipientID: recipientId,
225
- path: filePath
335
+ path: filePath,
336
+ fileSize
226
337
  });
227
- this.transporter.sendFile(file, {
228
- onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks) => {
229
- if (chunkIndex === 0) {
230
- this.progressBarManager.addTask({
231
- name: fileName,
232
- size: fileSize
233
- });
234
- }
338
+ await this.transporter.sendFile(file, {
339
+ onSendChunk: (transferId, chunkIndex, chunkSize, chunkData, totalChunks, completedChunks) => {
235
340
  return new Promise((resolve, reject) => {
236
341
  this.clientTide.emitEvent(clientID, "client-to-client-transfer", {
237
342
  senderID: clientID,
238
343
  recipientId,
239
344
  fileName,
240
345
  fileChunk: chunkData,
346
+ completedChunks,
241
347
  totalChunks,
242
348
  chunkIndex,
243
349
  chunkSize,
@@ -258,19 +364,17 @@ class FileMessagerClient {
258
364
  resolve();
259
365
  });
260
366
  },
261
- onProgress: (progress, chunkSize) => {
262
- this.progressBarManager.updateTaskProgress(fileName, chunkSize);
263
- },
264
367
  originDetails: {
265
368
  recipientID: recipientId,
266
369
  senderID: clientID
267
370
  },
268
371
  fileDetails: {
372
+ destinationPath,
269
373
  name: fileName,
270
374
  path: filePath,
271
375
  size: fileSize
272
376
  }
273
- }).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer: ${error}`, errorMessageColors));
377
+ }, this.clientTide).then(() => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ File ~ ${fileName} transfer complete!`, significantMessageColors)).catch(error => FileNetUtilityManager.outputGradient(`[FileTide ~ File Messager] ~ Refused file transfer ~ ${error}`, errorMessageColors));
274
378
  }
275
379
  }
276
380
  ;
@@ -15,6 +15,7 @@ const {
15
15
  const {
16
16
  FileTransferReceiverManager
17
17
  } = require("./HUDManagers/FileTransferReceiverManager.js");
18
+ const FileTransferRecoveryManager = require("./HUDManagers/FileTransferRecoveryManager.js");
18
19
  const {
19
20
  FileNetConfigManager
20
21
  } = require("./HUDManagers/FileNetConfigManager.js");
@@ -22,6 +23,7 @@ const {
22
23
  FileUtilityManager
23
24
  } = require("./HUDManagers/FileUtilityManager.js");
24
25
  const ConsoleProgressBarManager = require("./HUDComponents/ConsoleProgressBarManager");
26
+ const ChunkCapacitor = require("./HUDComponents/ChunkCapacitor.js");
25
27
  const ConsoleTable = require("./HUDComponents/ConsoleTable.js");
26
28
  const FileMessagerClient = require("./FileMessagerClient");
27
29
  const FileMessager = require("./FileMessager");
@@ -75,13 +77,20 @@ class FileTide {
75
77
  * @param {Function} onTransferProgress - Callback for when an current transfers progress
76
78
  * @param {Boolean} [enableTransferBarrier=true] - Whether to enable the transfer barrier
77
79
  * @param {Object} [options = {}] - Additional options
80
+ * @param {Object} [headers = {}] - Additional connection headers
81
+ * @param {Function} onConnect - Callback for when the client connects to the desired server
82
+ * @param {Function} onDisconnect - Callback for when the client disconnects from the desired server
83
+ * @param {String} authURL - The destination of the connection authentication functionality
84
+ * @param {Object} [authHeaders = {}] - Additional connection authentication headers
85
+ * @param {Boolean} [useAuthentication=true] - Whether to enable connection authentication
86
+ * @param {Boolean} [queryAuthToken=false] - Whether to enable connection authentication tokens
78
87
  */
79
- launchMessager(clientOptions = {}, connectionOptions = {}, roomName, userID, onLaunch, onIncomingFile, onIncomingTransfer, onTransferBarrier, onTransferProgress, enableTransferBarrier = true, options = {}) {
88
+ launchMessager(clientOptions = {}, connectionOptions = {}, roomName, userID, onLaunch, onIncomingFile, onIncomingTransfer, onTransferBarrier, onTransferProgress, enableTransferBarrier = true, options = {}, headers = {}, onConnect = null, onDisconnect = null, authURL = "", authHeaders = {}, useAuthentication = false, queryAuthToken = false) {
80
89
  if (this.clients.has(userID)) {
81
90
  this.stopMessager(userID);
82
91
  }
83
92
  const newClient = new FileMessagerClient(clientOptions);
84
- newClient.joinRoom(userID, roomName, connectionOptions);
93
+ newClient.joinRoom(userID, roomName, connectionOptions, headers, onConnect, onDisconnect, authURL, authHeaders, useAuthentication, queryAuthToken);
85
94
  this.transferBarrier = enableTransferBarrier;
86
95
  this.clientShorePolicies.set(userID, [FileUtilityManager.getTidePath(">CurrentUser"), FileUtilityManager.getTidePath(">Downloads"), FileUtilityManager.getTidePath(">Documents")]);
87
96
  this.clients.set(userID, newClient);
@@ -104,6 +113,9 @@ class FileTide {
104
113
  getTidePath(inputPath) {
105
114
  return FileUtilityManager.getTidePath(inputPath);
106
115
  }
116
+ saveTransferStates() {
117
+ FileTransferRecoveryManager.flushToDisk();
118
+ }
107
119
  filterFilesAndDirectories(dirPath, filterContent = [], callback) {
108
120
  FileUtilityManager.filterFilesAndDirectories(dirPath, filterContent, (files, error) => {
109
121
  if (callback) {
@@ -120,8 +132,8 @@ class FileTide {
120
132
  }
121
133
  clientMessager.listFiles(clientID, recipientID, depth);
122
134
  }
123
- async listFiles(directories, depth = 1) {
124
- return await FileUtilityManager.listFiles(directories, depth);
135
+ async listFiles(directories, depth = 1, onDirectory) {
136
+ return await FileUtilityManager.listFiles(directories, depth, onDirectory);
125
137
  }
126
138
  clearShorePolicies(clientID, shores = []) {
127
139
  let clientShorePolicies = this.clientShorePolicies.get(clientID);
@@ -153,8 +165,11 @@ class FileTide {
153
165
  * @param {Buffer} fileData - The path of the data to send
154
166
  * @param {String} destinationPath - The destination path on the device
155
167
  * @param {Array} filterDirectoryContent - List of paths to filter
168
+ * @param {Number} tideSize - The tide size in sending files (500)
169
+ * @param {Number} minTideSize - The minimum tide size in sending files (10)
170
+ * @param {Number} maxTideSize - The maximum tide size in sending files (2000)
156
171
  */
157
- async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = []) {
172
+ async sendToDevice(recipientID, filePath, destinationPath, filterDirectoryContent = [], tideSize = 500, minTideSize = 10, maxTideSize = 2000) {
158
173
  const clientID = this.currentUserID;
159
174
  const clientMessager = this.clients.get(clientID);
160
175
  if (!clientMessager) {
@@ -167,13 +182,18 @@ class FileTide {
167
182
  return;
168
183
  }
169
184
  if (fileDetails.type === "file") {
185
+ if (fileDetails.largeFile) {
186
+ FileTide.outputGradient(`[${clientID}] Sending large file ~ ${fileDetails.fileName} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
187
+ await clientMessager.sendLargeFile(clientID, recipientID, fileDetails.fileName, fileDetails.filePath, destinationPath, fileDetails.fileSize, tideSize, minTideSize, maxTideSize);
188
+ return;
189
+ }
170
190
  FileTide.outputGradient(`[${clientID}] Sending file ~ ${fileDetails.content.fileName} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
171
191
  clientMessager.sendFile(clientID, recipientID, fileDetails.content.fileName, fileDetails.content.fileData, destinationPath, fileDetails.content.size);
172
192
  return;
173
193
  }
174
194
  if (fileDetails.type === "directory") {
175
195
  FileTide.outputGradient(`[${clientID}] Sending ${fileDetails.content.length} files in directory ~ ${path.basename(filePath)} to client ~ ${recipientID}...`, ["#00C897", "#00E0FF"]);
176
- clientMessager.sendDirectoryFiles(clientID, recipientID, fileDetails.content, this.getTidePath(filePath), destinationPath);
196
+ clientMessager.sendDirectoryFiles(clientID, recipientID, fileDetails.content, this.getTidePath(filePath), destinationPath, tideSize, minTideSize, maxTideSize);
177
197
  return;
178
198
  }
179
199
  FileTide.outputGradient(`[FileTide] ~ No active client ~ ${recipientID} found.`, errorMessageColors);
@@ -241,7 +261,8 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
241
261
  onTransferRequested,
242
262
  onTransferStatus,
243
263
  onListFiles,
244
- onCurrentOnlineClients
264
+ onCurrentOnlineClients,
265
+ onClientTiding
245
266
  } = options;
246
267
  const cliTable = new ConsoleTable({
247
268
  padding: 2,
@@ -287,6 +308,11 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
287
308
  onCurrentOnlineClients(currentClients, true);
288
309
  }
289
310
  });
311
+ client.clientTide.onEvent(userID, "current-client-tiding", data => {
312
+ if (onClientTiding) {
313
+ onClientTiding(data.message, significantMessageColors);
314
+ }
315
+ });
290
316
  client.clientTide.onEvent(userID, "current-clients", data => {
291
317
  const currentClients = {};
292
318
  Object.keys(data).forEach(key => {
@@ -302,6 +328,23 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
302
328
  onCurrentOnlineClients(currentClients, false);
303
329
  }
304
330
  });
331
+ client.clientTide.onEvent(userID, "save-current-transfer-states", async data => {
332
+ this.saveTransferStates();
333
+ return;
334
+ });
335
+ client.clientTide.onEvent(userID, "current-transfer-state", async data => {
336
+ const {
337
+ senderID,
338
+ filePath
339
+ } = data;
340
+ const currentTransferState = FileTransferRecoveryManager.getTransferState(userID, senderID, filePath);
341
+ client.clientTide.emitEvent(userID, "send-current-transfer-state", {
342
+ recipientID: userID,
343
+ senderID,
344
+ transferState: currentTransferState
345
+ });
346
+ return;
347
+ });
305
348
  client.clientTide.onEvent(userID, "queue-incoming-transfer", async data => {
306
349
  if (this.transferBarrier && onIncomingTransfer) {
307
350
  onIncomingTransfer(data, status => {
@@ -320,6 +363,16 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
320
363
  return;
321
364
  });
322
365
  client.clientTide.onEvent(userID, "incoming-transfer", async data => {
366
+ const {
367
+ currentSender
368
+ } = data;
369
+ if (currentSender && currentSender === userID) {
370
+ _FileTide.outputGradient(`[${userID}] Transfer successfully sent to ${data.recipientID}`, significantMessageColors);
371
+ this.progressBarManager.completionMessage = `Successfully sent to ${data.recipientID}!`;
372
+ this.progressBarManager.displayPage("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta);
373
+ this.progressBarManager.listenForInput("Page Number:", "Press 'n' for next page, 'p' for previous page.", chalk.blue, chalk.magenta, chalk.yellow);
374
+ return;
375
+ }
323
376
  _FileTide.outputGradient(`[${userID}] Preparing to receive transfer : ${FileUtilityManager.getTidePath(data.path)}`, significantMessageColors);
324
377
  if (onTransferStart) {
325
378
  onTransferStart(data);
@@ -371,11 +424,29 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
371
424
  }
372
425
  await fileTransferReceiver.handleTransferProgress(data);
373
426
  this.progressBarManager.updateTaskProgress(data.fileName, data.chunkSize || 512 * 1024);
427
+ const currentTransferState = FileTransferRecoveryManager.getTransferState(userID, data.senderName, data.path) || {
428
+ transferredSize: 0
429
+ };
430
+ currentTransferState.transferredSize += data.chunkSize || 512 * 1024;
431
+ FileTransferRecoveryManager.saveTransferState(userID, data.senderName, data.path, data.chunkIndex, currentTransferState.transferredSize);
374
432
  if (onTransferProgress) {
375
433
  onTransferProgress(data);
376
434
  }
377
435
  });
378
436
  client.clientTide.onEvent(userID, "transfer-status", data => {
437
+ const {
438
+ chunkIndex,
439
+ chunkSize,
440
+ fileName,
441
+ fileSize
442
+ } = data;
443
+ if (chunkIndex === 0) {
444
+ this.progressBarManager.addTask({
445
+ name: fileName,
446
+ size: fileSize
447
+ });
448
+ }
449
+ this.progressBarManager.updateTaskProgress(fileName, chunkSize);
379
450
  if (onTransferStatus) {
380
451
  onTransferStatus(data);
381
452
  }
@@ -389,11 +460,12 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
389
460
  if (!transferState) {
390
461
  return;
391
462
  }
392
- fileTransferReceiver.completeTransfer(data, (transferData, transfers, success) => {
463
+ fileTransferReceiver.completeTransfer(data, (transferData, transfers, success, error) => {
393
464
  if (success) {
465
+ FileTransferRecoveryManager.clearTransferState(userID, data.senderID, data.filePath);
394
466
  _FileTide.outputGradient(`[${userID}] File saved at : ${savePath}\n`, significantMessageColors);
395
467
  } else if (!success) {
396
- _FileTide.outputGradient(`[${userID}] Did not save file at : ${savePath}\n`, errorMessageColors);
468
+ _FileTide.outputGradient(`[${userID}] Did not save file at : ${savePath}\n\t${error}\n`, errorMessageColors);
397
469
  }
398
470
  if (onTransferComplete) {
399
471
  onTransferComplete(data, fileTransferReceiver.activeTransfers, success);
@@ -416,17 +488,29 @@ function _setupFileEventListeners(userID, client, onTransferProgress, onIncoming
416
488
  return;
417
489
  });
418
490
  client.clientTide.onEvent(userID, "list-files", async data => {
419
- _FileTide.outputGradient(`[${userID}] Sending file list!`, significantMessageColors);
491
+ _FileTide.outputGradient(`[${userID}] Sending file list to ${data.senderID}...!`, significantMessageColors);
420
492
  const currentClientShorePolicies = this.getShorePolicies(userID);
421
- const listedFiles = await this.listFiles(currentClientShorePolicies || [">Downloads"], data.depth || 1);
422
- client.clientTide.emitEvent(userID, "send-file-list-to-client", {
423
- senderID: data.recipientID,
424
- recipientID: data.senderID,
425
- listedFiles
493
+ const listedFiles = await this.listFiles(currentClientShorePolicies || [">Downloads"], data.depth || 1, (totalItems, directoryContents) => {
494
+ const capacitor = new ChunkCapacitor(directoryContents, 100);
495
+ const contents = capacitor.getAllChunks();
496
+ for (let files of contents) {
497
+ client.clientTide.emitEvent(userID, "send-file-list-to-client", {
498
+ senderID: data.recipientID,
499
+ recipientID: data.senderID,
500
+ totalItems: files.length,
501
+ listedFiles: files
502
+ });
503
+ }
426
504
  });
505
+ setTimeout(() => {
506
+ _FileTide.outputGradient(`[${userID}] Sent file list to ${data.senderID}!`, significantMessageColors);
507
+ }, 1000);
508
+ });
509
+ client.clientTide.onEvent(userID, "error", async error => {
510
+ _FileTide.outputGradient(`[${userID} | FileTide Error] ${error}`, errorMessageColors);
427
511
  });
428
512
  client.clientTide.onEvent(userID, "client-file-list", async data => {
429
- _FileTide.outputGradient(`[${userID}] Listed ${data.senderID}'s files!`, significantMessageColors);
513
+ _FileTide.outputGradient(`[${userID}] Received ${data.totalItems} directory contents from ${data.senderID}'s file list!`, significantMessageColors);
430
514
  if (onListFiles) {
431
515
  onListFiles(data);
432
516
  }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ class ChunkCapacitor {
4
+ constructor(list, chunkSize = 100) {
5
+ this.list = list;
6
+ this.chunkSize = chunkSize;
7
+ this.chunks = this.createChunks();
8
+ }
9
+ createChunks() {
10
+ const chunks = [];
11
+ for (let i = 0; i < this.list.length; i += this.chunkSize) {
12
+ const chunk = this.list.slice(i, i + this.chunkSize);
13
+ chunks.push(chunk);
14
+ }
15
+ return chunks;
16
+ }
17
+ getChunk(index) {
18
+ if (index < 0 || index >= this.chunks.length) {
19
+ console.log("Chunk index out of bounds.");
20
+ return null;
21
+ }
22
+ return this.chunks[index];
23
+ }
24
+ getTotalChunks() {
25
+ return this.chunks.length;
26
+ }
27
+ getTotalItems() {
28
+ return this.list.length;
29
+ }
30
+ getAllChunks() {
31
+ return this.chunks;
32
+ }
33
+ }
34
+ ;
35
+ module.exports = ChunkCapacitor;
@@ -123,7 +123,11 @@ class ConsoleProgressBarManager {
123
123
  newBar.speed = speed;
124
124
  const filledLength = Math.round(settings.barLength * (newBar.current / newBar.totalSize));
125
125
  const bar = settings.barColor(settings.filledChar.repeat(filledLength)) + settings.unfilledChar.repeat(settings.barLength - filledLength);
126
- process.stdout.write(`${settings.taskNameColor(newBar.taskName)} > ${settings.totalSizeColor(newBar.getFormattedSize(newBar.totalSize))} | ${settings.sizeTransferredColor(newBar.getFormattedSize(newBar.current))} ${settings.speedColor(`${speedData.current} ${speedData.unit}/s`)} | ${settings.taskETAColor(this.convertSeconds(timeLeft.toFixed(2)))} [${bar}] ${settings.percentageColor(percentage.toFixed(2) + "%")}`);
126
+ try {
127
+ process.stdout.write(`${settings.taskNameColor(newBar.taskName)} > ${settings.totalSizeColor(newBar.getFormattedSize(newBar.totalSize))} | ${settings.sizeTransferredColor(newBar.getFormattedSize(newBar.current))} ${settings.speedColor(`${speedData.current} ${speedData.unit}/s`)} | ${settings.taskETAColor(this.convertSeconds(timeLeft.toFixed(2)))} [${bar}] ${settings.percentageColor(percentage.toFixed(2) + "%")}`);
128
+ } catch (error) {
129
+ console.log(error);
130
+ }
127
131
  },
128
132
  update: amount => {
129
133
  newBar.current += amount;