-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathremote.lua
More file actions
279 lines (248 loc) · 8.42 KB
/
Copy pathremote.lua
File metadata and controls
279 lines (248 loc) · 8.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
local json = require("json")
local InfoMessage = require("ui/widget/infomessage")
local UIManager = require("ui/uimanager")
local T = require("ffi/util").template
local _ = require("gettext")
local util = require("util")
local annotations = require("annotations")
local utils = require("utils")
local has_syncservice, SyncService = pcall(require, "apps/cloudstorage/syncservice")
local M = {}
local function get_sync_provider(widget)
if widget.ui.cloudstorage then
return widget.ui.cloudstorage
elseif has_syncservice then
return SyncService
end
return nil
end
local function perform_sync(widget, json_path, sync_cb, is_silent, on_complete)
local provider = get_sync_provider(widget)
if not provider then
UIManager:show(InfoMessage:new {
text = _("Cloud Storage plugin is not enabled or available."),
timeout = 4
})
if on_complete then
on_complete(false)
end
return
end
local server = widget.settings.sync_server
if server then
if widget.ui.cloudstorage then
widget.ui.cloudstorage:sync(server, json_path, sync_cb, is_silent)
else
SyncService.sync(server, json_path, sync_cb, is_silent)
end
else
UIManager:show(InfoMessage:new {
text = T(_("No cloud destination set in settings.")),
timeout = 4
})
if on_complete then
on_complete(false)
end
end
end
function M.sync_annotations(widget, document, json_path, on_complete, force)
local sync_cb = function(local_file, cached_file, income_file)
local success, merged_list = annotations.sync_callback(document, local_file, cached_file, income_file, force)
if on_complete then
on_complete(success, merged_list)
end
return success
end
perform_sync(widget, json_path, sync_cb, not force, on_complete)
end
function M._sync_progress_callback(local_file, cached_file, income_file)
local local_data = utils.read_json(local_file) or {}
local income_data = utils.read_json(income_file) or {}
local_data = M._normalize_progress(local_data)
income_data = M._normalize_progress(income_data)
local changed = false
for device_id, data in pairs(income_data) do
if not local_data[device_id] or (data.timestamp or "") > (local_data[device_id].timestamp or "") then
local_data[device_id] = data
changed = true
end
end
if changed then
util.writeToFile(json.encode(local_data), local_file, true, false, true)
end
return true, local_data
end
local function run_silent(func, on_timeout)
local old_show = UIManager.show
local new_show
new_show = function(self, widget_item)
if widget_item.text == _("Successfully synchronized.") then
return
end
return old_show(self, widget_item)
end
UIManager.show = new_show
local restored = false
local function restore()
if not restored then
restored = true
if UIManager.show == new_show then
UIManager.show = old_show
end
end
end
-- Timeout safety fallback (15 seconds)
UIManager:scheduleIn(15, function()
if not restored then
if on_timeout then
on_timeout()
end
restore()
end
end)
func(restore)
end
function M.push_progress(widget, json_path, on_complete)
if not widget.ui.cloudstorage then
if on_complete then
on_complete(false)
end
return
end
local server = widget.settings.sync_server
if server then
local completed = false
local cb_called = false
local function on_complete_once(success)
if not completed then
completed = true
if on_complete then
on_complete(success)
end
end
end
run_silent(function(restore)
local success = widget.ui.cloudstorage:sync(server, json_path, function(local_file, cached_file, income_file)
cb_called = true
local success, local_data = M._sync_progress_callback(local_file, cached_file, income_file)
on_complete_once(success)
UIManager:nextTick(restore)
return success
end, true) -- is_silent = true
if success == false then
on_complete_once(false)
restore()
elseif not cb_called and success ~= nil then
on_complete_once(false)
restore()
end
end, function()
on_complete_once(false)
end)
else
if on_complete then
on_complete(false)
end
end
end
function M.push_progress_bg(widget, json_path, on_complete)
if not widget.ui.cloudstorage then
if on_complete then
on_complete(false)
end
return
end
local server = widget.settings.sync_server
if server then
local Trapper = require("ui/trapper")
local logger = require("logger")
Trapper:wrap(function()
local completed, success = Trapper:dismissableRunInSubprocess(function()
local sync_success = false
run_silent(function(restore)
local res = widget.ui.cloudstorage:sync(server, json_path, function(local_file, cached_file, income_file)
sync_success = M._sync_progress_callback(local_file, cached_file, income_file)
UIManager:nextTick(restore)
return sync_success
end, true)
if res == false then
restore()
end
end)
return sync_success
end, false)
if completed and not success then
logger.info("AnnotationSync: background progress sync failed/unsupported, falling back to in-process sync")
M.push_progress(widget, json_path, on_complete)
else
if on_complete then
on_complete(completed and success)
end
end
end)
else
if on_complete then
on_complete(false)
end
end
end
function M.pull_progress(widget, json_path, on_complete)
if not widget.ui.cloudstorage then
if on_complete then
on_complete(false)
end
return
end
local server = widget.settings.sync_server
if server then
widget.ui.cloudstorage:sync(server, json_path, function(local_file, cached_file, income_file)
local success, local_data = M._sync_progress_callback(local_file, cached_file, income_file)
if on_complete then
on_complete(success, local_data)
end
return success -- Push merged back to remote
end, false) -- is_silent = false
else
if on_complete then
on_complete(false)
end
end
end
function M._normalize_progress(data)
if data.device and data.page then
-- Old format
local device_id = data.device
return {
[device_id] = {
page = data.page,
percentage = data.percentage,
pos = data.pos, -- Ensure pos is preserved
timestamp = data.timestamp,
}
}
end
return data
end
function M._sync_settings_callback(widget, local_file, cached_file, income_file)
local local_data = utils.read_json(local_file) or {}
local income_data = utils.read_json(income_file) or {}
-- Merge incoming settings from other devices
for device_id, data in pairs(income_data) do
if device_id ~= widget.manager:getDeviceName() then
local_data[device_id] = data
end
end
util.writeToFile(json.encode(local_data), local_file, true, false, true)
return true, local_data
end
function M.sync_settings(widget, json_path, on_complete)
local sync_cb = function(local_file, cached_file, income_file)
local success, local_data = M._sync_settings_callback(widget, local_file, cached_file, income_file)
if on_complete then
on_complete(success, local_data)
end
return success
end
perform_sync(widget, json_path, sync_cb, false, on_complete)
end
return M