mirror of
https://github.com/ankitects/anki.git
synced 2025-09-20 15:02:21 -04:00
detect sync conflicts and offer choice of direction
This commit is contained in:
parent
9ca3430f3d
commit
e3f50540d5
3 changed files with 80 additions and 3 deletions
|
@ -2168,6 +2168,7 @@ it to your friends.
|
|||
self.connect(self.syncThread, SIGNAL("fullSyncFinished"), self.fullSyncFinished)
|
||||
self.connect(self.syncThread, SIGNAL("fullSyncProgress"), self.fullSyncProgress)
|
||||
self.connect(self.syncThread, SIGNAL("badUserPass"), self.badUserPass)
|
||||
self.connect(self.syncThread, SIGNAL("syncConflicts"), self.onConflict)
|
||||
self.syncThread.start()
|
||||
self.switchToWelcomeScreen()
|
||||
self.setEnabled(False)
|
||||
|
@ -2185,6 +2186,22 @@ it to your friends.
|
|||
ok.append(d)
|
||||
return ok
|
||||
|
||||
def onConflict(self, deckName):
|
||||
diag = ui.utils.askUserDialog(_("""\
|
||||
<b>%s</b> has been changed on both the local<br>
|
||||
and remote side. What do you want to do?""" % deckName),
|
||||
[_("Keep Local"),
|
||||
_("Keep Remote"),
|
||||
_("Cancel")])
|
||||
diag.setDefault(2)
|
||||
ret = diag.run()
|
||||
if ret == _("Keep Local"):
|
||||
self.syncThread.conflictResolution = "keepLocal"
|
||||
elif ret == _("Keep Remote"):
|
||||
self.syncThread.conflictResolution = "keepRemote"
|
||||
else:
|
||||
self.syncThread.conflictResolution = "cancel"
|
||||
|
||||
def onSyncFinished(self):
|
||||
"Reopen after sync finished."
|
||||
self.mainWin.buttonStack.show()
|
||||
|
|
|
@ -100,7 +100,8 @@ class Sync(QThread):
|
|||
# multi-mode setup
|
||||
if deck:
|
||||
c = sqlite.connect(deck)
|
||||
syncName = c.execute("select syncName from decks").fetchone()[0]
|
||||
(syncName, localChanged) = c.execute(
|
||||
"select syncName, modified > lastSync from decks").fetchone()
|
||||
c.close()
|
||||
if not syncName:
|
||||
return
|
||||
|
@ -108,6 +109,10 @@ class Sync(QThread):
|
|||
else:
|
||||
syncName = self.parent.syncName
|
||||
path = self.parent.deckPath
|
||||
c = sqlite.connect(path)
|
||||
localChanged = c.execute(
|
||||
"select modified > lastSync from decks").fetchone()[0]
|
||||
c.close()
|
||||
# ensure deck mods cached
|
||||
try:
|
||||
proxy = self.connect()
|
||||
|
@ -127,12 +132,25 @@ class Sync(QThread):
|
|||
self.emit(SIGNAL("noMatchingDeck"), keys, not self.onlyMerge)
|
||||
self.setStatus("")
|
||||
return
|
||||
self.setStatus(_("Syncing <b>%s</b>...") % syncName, 0)
|
||||
# check clock
|
||||
timediff = abs(proxy.timestamp - time.time())
|
||||
if timediff > 300:
|
||||
self.emit(SIGNAL("syncClockOff"), timediff)
|
||||
return
|
||||
# check conflicts
|
||||
remoteChanged = proxy.hasChanged(syncName)
|
||||
self.conflictResolution = None
|
||||
if localChanged and remoteChanged:
|
||||
self.emit(SIGNAL("syncConflicts"), syncName)
|
||||
while not self.conflictResolution:
|
||||
time.sleep(0.2)
|
||||
if self.conflictResolution == "cancel":
|
||||
if not deck:
|
||||
# alert we're finished early
|
||||
self.emit(SIGNAL("syncFinished"))
|
||||
return
|
||||
# reopen
|
||||
self.setStatus(_("Syncing <b>%s</b>...") % syncName, 0)
|
||||
self.deck = None
|
||||
try:
|
||||
self.deck = DeckStorage.Deck(path)
|
||||
|
@ -146,8 +164,12 @@ class Sync(QThread):
|
|||
# summary
|
||||
self.setStatus(_("Fetching summary from server..."), 0)
|
||||
sums = client.summaries()
|
||||
if client.needFullSync(sums):
|
||||
if self.conflictResolution or client.needFullSync(sums):
|
||||
self.setStatus(_("Preparing full sync..."), 0)
|
||||
if self.conflictResolution == "keepLocal":
|
||||
client.remoteTime = 0
|
||||
elif self.conflictResolution == "keepRemote":
|
||||
client.localTime = 0
|
||||
ret = client.prepareFullSync()
|
||||
if ret[0] == "fromLocal":
|
||||
self.setStatus(_("Uploading..."), 0)
|
||||
|
|
|
@ -75,6 +75,44 @@ def askUser(text, parent=None, help=""):
|
|||
break
|
||||
return r == QMessageBox.Yes
|
||||
|
||||
class ButtonedDialog(QMessageBox):
|
||||
|
||||
def __init__(self, text, buttons, parent=None, help=""):
|
||||
QDialog.__init__(self, parent)
|
||||
self.buttons = []
|
||||
self.setWindowTitle("Anki")
|
||||
self.help = help
|
||||
self.setIcon(QMessageBox.Warning)
|
||||
self.setText(text)
|
||||
# v = QVBoxLayout()
|
||||
# v.addWidget(QLabel(text))
|
||||
# box = QDialogButtonBox()
|
||||
# v.addWidget(box)
|
||||
for b in buttons:
|
||||
self.buttons.append(
|
||||
self.addButton(b, QMessageBox.AcceptRole))
|
||||
if help:
|
||||
self.addButton(_("Help"), QMessageBox.HelpRole)
|
||||
buttons.append(_("Help"))
|
||||
#self.setLayout(v)
|
||||
|
||||
def run(self):
|
||||
self.exec_()
|
||||
but = self.clickedButton().text()
|
||||
if but == "Help":
|
||||
# FIXME stop dialog closing?
|
||||
openWikiLink(self.help)
|
||||
return self.clickedButton().text()
|
||||
|
||||
def setDefault(self, idx):
|
||||
self.setDefaultButton(self.buttons[idx])
|
||||
|
||||
def askUserDialog(text, buttons, parent=None, help=""):
|
||||
if not parent:
|
||||
parent = ankiqt.mw
|
||||
diag = ButtonedDialog(text, buttons, parent, help)
|
||||
return diag
|
||||
|
||||
class GetTextDialog(QDialog):
|
||||
|
||||
def __init__(self, parent, question, help=None, edit=None):
|
||||
|
|
Loading…
Reference in a new issue