mirror of
https://github.com/zero-peak/ZeroOmega.git
synced 2025-01-22 15:08:12 -05:00
Use Gist as a new synchronization method
This commit is contained in:
parent
3994518c00
commit
6f4a579c18
@ -42,23 +42,24 @@ class LocalStorage {
|
|||||||
}
|
}
|
||||||
const instance = new LocalStorage()
|
const instance = new LocalStorage()
|
||||||
|
|
||||||
if (!globalThis.localStorage) {
|
globalThis.zeroLocalStorage = new Proxy(instance, {
|
||||||
globalThis.localStorage = new Proxy(instance, {
|
set: function (obj, prop, value) {
|
||||||
set: function (obj, prop, value) {
|
if (LocalStorage.prototype.hasOwnProperty(prop)) {
|
||||||
if (LocalStorage.prototype.hasOwnProperty(prop)) {
|
instance[prop] = value
|
||||||
instance[prop] = value
|
} else {
|
||||||
} else {
|
instance.setItem(prop, value)
|
||||||
instance.setItem(prop, value)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
get: function (target, name) {
|
|
||||||
if (LocalStorage.prototype.hasOwnProperty(name)) {
|
|
||||||
return instance[name]
|
|
||||||
}
|
|
||||||
if (valuesMap.has(name)) {
|
|
||||||
return instance.getItem(name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
return true
|
||||||
|
},
|
||||||
|
get: function (target, name) {
|
||||||
|
if (LocalStorage.prototype.hasOwnProperty(name)) {
|
||||||
|
return instance[name]
|
||||||
|
}
|
||||||
|
if (valuesMap.has(name)) {
|
||||||
|
return instance.getItem(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!globalThis.localStorage) {
|
||||||
|
globalThis.localStorage = globalThis.zeroLocalStorage;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
const logStore = idbKeyval.createStore('log-store', 'log-store');
|
const logStore = idbKeyval.createStore('log-store', 'log-store');
|
||||||
|
|
||||||
const dayOfWeek = moment().format('E') // Day of Week (ISO), keep logs max 7 day
|
|
||||||
const logKey = 'zerolog-' + dayOfWeek
|
|
||||||
const logSequence = []
|
const logSequence = []
|
||||||
let isRunning = false
|
let isRunning = false
|
||||||
let splitStr = '\n------------------\n'
|
let splitStr = '\n------------------\n'
|
||||||
@ -12,15 +10,21 @@ const originConsoleError = console.error
|
|||||||
const _logFn = async function(){
|
const _logFn = async function(){
|
||||||
if (isRunning) return
|
if (isRunning) return
|
||||||
isRunning = true
|
isRunning = true
|
||||||
|
const _moment = moment()
|
||||||
|
|
||||||
|
const dayOfWeek = _moment.format('E') // Day of Week (ISO), keep logs max 7 day
|
||||||
|
const monthNum = _moment.format('DD')
|
||||||
|
const logKey = 'zerolog-' + dayOfWeek
|
||||||
while (logSequence.length > 0) {
|
while (logSequence.length > 0) {
|
||||||
const str = logSequence.join('\n');
|
const str = logSequence.join('\n');
|
||||||
logSequence.length = 0;
|
logSequence.length = 0;
|
||||||
let logInfo = await idbKeyval.get(logKey, logStore)
|
let logInfo = await idbKeyval.get(logKey, logStore)
|
||||||
|
let date = _moment.format('YYYY-MM-DD')
|
||||||
if (!logInfo || !logInfo.date) {
|
if (!logInfo || !logInfo.date) {
|
||||||
logInfo = { date: moment().format('YYYY-MM-DD'), val: ''}
|
logInfo = { date: date, val: ''}
|
||||||
}
|
}
|
||||||
let { date, val } = logInfo
|
let { val } = logInfo
|
||||||
if ( !date.endsWith(dayOfWeek)) {
|
if ( logInfo.date != date) {
|
||||||
val = ''
|
val = ''
|
||||||
}
|
}
|
||||||
val += splitStr
|
val += splitStr
|
||||||
@ -43,6 +47,9 @@ const replacerFn = (key, value)=>{
|
|||||||
case 'password':
|
case 'password':
|
||||||
case 'host':
|
case 'host':
|
||||||
case 'port':
|
case 'port':
|
||||||
|
case 'token':
|
||||||
|
case 'gistToken':
|
||||||
|
case 'gistId':
|
||||||
return '<secret>'
|
return '<secret>'
|
||||||
default:
|
default:
|
||||||
return value
|
return value
|
||||||
@ -84,7 +91,7 @@ const _lastErrorLogFn = async ()=>{
|
|||||||
_lastErrorLogFn.isRunning = false
|
_lastErrorLogFn.isRunning = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastErrorLogFn = async ()=>{
|
const lastErrorLogFn = async function (){
|
||||||
const val = getStr.apply(null, arguments)
|
const val = getStr.apply(null, arguments)
|
||||||
_lastErrorLogFn.val = val
|
_lastErrorLogFn.val = val
|
||||||
_lastErrorLogFn()
|
_lastErrorLogFn()
|
||||||
|
@ -154,13 +154,11 @@ actionForUrl = (url) ->
|
|||||||
|
|
||||||
|
|
||||||
storage = new OmegaTargetCurrent.Storage('local')
|
storage = new OmegaTargetCurrent.Storage('local')
|
||||||
state = new OmegaTargetCurrent.BrowserStorage(localStorage, 'omega.local.')
|
state = new OmegaTargetCurrent.BrowserStorage(zeroLocalStorage, 'omega.local.')
|
||||||
|
|
||||||
if chrome?.storage?.sync or browser?.storage?.sync
|
if chrome?.storage?.sync or browser?.storage?.sync
|
||||||
syncStorage = new OmegaTargetCurrent.Storage('sync')
|
syncStorage = new OmegaTargetCurrent.SyncStorage('sync', state)
|
||||||
sync = new OmegaTargetCurrent.OptionsSync(syncStorage)
|
sync = new OmegaTargetCurrent.OptionsSync(syncStorage)
|
||||||
if localStorage['omega.local.syncOptions'] != '"sync"'
|
|
||||||
sync.enabled = false
|
|
||||||
sync.transformValue = OmegaTargetCurrent.Options.transformValueForSync
|
sync.transformValue = OmegaTargetCurrent.Options.transformValueForSync
|
||||||
|
|
||||||
proxyImpl = OmegaTargetCurrent.proxy.getProxyImpl(Log)
|
proxyImpl = OmegaTargetCurrent.proxy.getProxyImpl(Log)
|
||||||
@ -315,10 +313,23 @@ refreshActivePageIfEnabled = ->
|
|||||||
else
|
else
|
||||||
chrome.tabs.reload(tabs[0].id, {bypassCache: true})
|
chrome.tabs.reload(tabs[0].id, {bypassCache: true})
|
||||||
|
|
||||||
|
|
||||||
|
resetAllOptions = ->
|
||||||
|
options.ready.then ->
|
||||||
|
options._watchStop?()
|
||||||
|
options._syncWatchStop?()
|
||||||
|
Promise.all([
|
||||||
|
chrome.storage.sync.clear(),
|
||||||
|
chrome.storage.local.clear()
|
||||||
|
])
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener (request, sender, respond) ->
|
chrome.runtime.onMessage.addListener (request, sender, respond) ->
|
||||||
return unless request and request.method
|
return unless request and request.method
|
||||||
options.ready.then ->
|
options.ready.then ->
|
||||||
if request.method == 'getState'
|
if request.method == 'resetAllOptions'
|
||||||
|
target = globalThis
|
||||||
|
method = resetAllOptions
|
||||||
|
else if request.method == 'getState'
|
||||||
target = state
|
target = state
|
||||||
method = state.get
|
method = state.get
|
||||||
else if request.method == 'setState'
|
else if request.method == 'setState'
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
|
|
||||||
logStore = idbKeyval.createStore('log-store', 'log-store')
|
logStore = idbKeyval.createStore('log-store', 'log-store')
|
||||||
|
syncStore = idbKeyval.createStore('sync-store', 'sync')
|
||||||
|
|
||||||
isProcessing = false
|
waitTimeFn = (timeout = 1000) ->
|
||||||
|
return new Promise((resolve, reject) ->
|
||||||
|
setTimeout( ->
|
||||||
|
resolve()
|
||||||
|
, timeout)
|
||||||
|
)
|
||||||
|
|
||||||
window.OmegaDebug =
|
window.OmegaDebug =
|
||||||
getProjectVersion: ->
|
getProjectVersion: ->
|
||||||
@ -9,8 +15,6 @@ window.OmegaDebug =
|
|||||||
getExtensionVersion: ->
|
getExtensionVersion: ->
|
||||||
chrome.runtime.getManifest().version
|
chrome.runtime.getManifest().version
|
||||||
downloadLog: ->
|
downloadLog: ->
|
||||||
return if isProcessing
|
|
||||||
isProcessing = true
|
|
||||||
idbKeyval.entries(logStore).then((entries) ->
|
idbKeyval.entries(logStore).then((entries) ->
|
||||||
zip = new JSZip()
|
zip = new JSZip()
|
||||||
zipFolder = zip.folder('ZeroOmega')
|
zipFolder = zip.folder('ZeroOmega')
|
||||||
@ -28,26 +32,30 @@ window.OmegaDebug =
|
|||||||
).then((blob) ->
|
).then((blob) ->
|
||||||
filename = "ZeroOmegaLog_#{Date.now()}.zip"
|
filename = "ZeroOmegaLog_#{Date.now()}.zip"
|
||||||
saveAs(blob, filename)
|
saveAs(blob, filename)
|
||||||
isProcessing = false
|
|
||||||
)
|
)
|
||||||
resetOptions: ->
|
resetOptions: ->
|
||||||
return if isProcessing
|
chrome.runtime.sendMessage({
|
||||||
isProcessing = true
|
method: 'resetAllOptions'
|
||||||
Promise.all([
|
}, (response) ->
|
||||||
idbKeyval.clear(logStore),
|
# firefox still use localStorage
|
||||||
chrome.storage.local.clear()
|
|
||||||
]).then( ->
|
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
# Prevent options loading from sync storage after reload.
|
# as storage watch value changed
|
||||||
localStorage['omega.local.syncOptions'] = '"conflict"'
|
# and background localStorage state delay saved
|
||||||
isProcessing = false
|
# this must after storage and wait 2 seconds
|
||||||
chrome.runtime.reload()
|
Promise.all([
|
||||||
|
idbKeyval.clear(logStore),
|
||||||
|
idbKeyval.clear(syncStore),
|
||||||
|
waitTimeFn(2000)
|
||||||
|
]).then( ->
|
||||||
|
idbKeyval.clear()
|
||||||
|
).then( ->
|
||||||
|
# Prevent options loading from sync storage after reload.
|
||||||
|
#localStorage['omega.local.syncOptions'] = '"conflict"'
|
||||||
|
chrome.runtime.reload()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
reportIssue: ->
|
reportIssue: ->
|
||||||
return if isProcessing
|
|
||||||
isProcessing = true
|
|
||||||
idbKeyval.get('lastError', logStore).then((lastError) ->
|
idbKeyval.get('lastError', logStore).then((lastError) ->
|
||||||
isProcessing = false
|
|
||||||
url = 'https://github.com/suziwen/ZeroOmega/issues/new?title=&body='
|
url = 'https://github.com/suziwen/ZeroOmega/issues/new?title=&body='
|
||||||
finalUrl = url
|
finalUrl = url
|
||||||
try
|
try
|
||||||
|
@ -158,7 +158,8 @@ angular.module('omegaTarget', []).factory 'omegaTarget', ($q) ->
|
|||||||
chrome.tabs.create url: 'chrome://extensions/configureCommands'
|
chrome.tabs.create url: 'chrome://extensions/configureCommands'
|
||||||
setOptionsSync: (enabled, args) ->
|
setOptionsSync: (enabled, args) ->
|
||||||
callBackground('setOptionsSync', enabled, args)
|
callBackground('setOptionsSync', enabled, args)
|
||||||
resetOptionsSync: (enabled, args) -> callBackground('resetOptionsSync')
|
resetOptionsSync: (args) -> callBackground('resetOptionsSync', args)
|
||||||
|
checkOptionsSyncChange: -> callBackground('checkOptionsSyncChange')
|
||||||
setRequestInfoCallback: (callback) ->
|
setRequestInfoCallback: (callback) ->
|
||||||
requestInfoCallback = callback
|
requestInfoCallback = callback
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
module.exports =
|
module.exports =
|
||||||
Storage: require('./storage')
|
Storage: require('./storage')
|
||||||
|
SyncStorage: require('./sync_storage')
|
||||||
Options: require('./options')
|
Options: require('./options')
|
||||||
ChromeTabs: require('./tabs')
|
ChromeTabs: require('./tabs')
|
||||||
SwitchySharp: require('./switchysharp')
|
SwitchySharp: require('./switchysharp')
|
||||||
|
384
omega-target-chromium-extension/src/module/sync_storage.coffee
Normal file
384
omega-target-chromium-extension/src/module/sync_storage.coffee
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
OmegaTarget = require('omega-target')
|
||||||
|
Promise = OmegaTarget.Promise
|
||||||
|
|
||||||
|
|
||||||
|
onChangedListenerInstalled = false
|
||||||
|
isPulling = false
|
||||||
|
isPushing = false
|
||||||
|
|
||||||
|
state = null
|
||||||
|
|
||||||
|
optionFilename = 'ZeroOmega.json'
|
||||||
|
gistId = ''
|
||||||
|
gistToken = ''
|
||||||
|
gistHost = 'https://api.github.com'
|
||||||
|
|
||||||
|
processCheckCommit = ->
|
||||||
|
getLastCommit(gistId).then((remoteCommit) ->
|
||||||
|
state.set({
|
||||||
|
'lastGistSync': Date.now()
|
||||||
|
}).then(->
|
||||||
|
state.get({'lastGistCommit': '-2'}).then(({ lastGistCommit }) ->
|
||||||
|
return lastGistCommit isnt remoteCommit
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).catch( ->
|
||||||
|
return true
|
||||||
|
)
|
||||||
|
|
||||||
|
processPull = (syncStore) ->
|
||||||
|
return new Promise((resolve, reject) ->
|
||||||
|
getGist(gistId).then((gist) ->
|
||||||
|
if isPushing
|
||||||
|
resolve({changes: {}})
|
||||||
|
else
|
||||||
|
changes = {}
|
||||||
|
getAll(syncStore).then((data) ->
|
||||||
|
try
|
||||||
|
optionsStr = gist.files[optionFilename]?.content
|
||||||
|
options = JSON.parse(optionsStr)
|
||||||
|
for own key, val of data
|
||||||
|
changes[key] = {
|
||||||
|
oldValue: val
|
||||||
|
}
|
||||||
|
for own key, val of options
|
||||||
|
target = changes[key]
|
||||||
|
unless target
|
||||||
|
changes[key] = {}
|
||||||
|
target = changes[key]
|
||||||
|
target.newValue = val
|
||||||
|
for own key,val of changes
|
||||||
|
if JSON.stringify(val.oldValue) is JSON.stringify(val.newValue)
|
||||||
|
delete changes[key]
|
||||||
|
catch e
|
||||||
|
changes = {}
|
||||||
|
state?.set({
|
||||||
|
'lastGistCommit': gist.history[0]?.version
|
||||||
|
'lastGistState': 'success'
|
||||||
|
'lastGistSync': Date.now()
|
||||||
|
})
|
||||||
|
resolve({
|
||||||
|
changes: changes,
|
||||||
|
remoteOptions: options
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).catch((e) ->
|
||||||
|
state?.set({
|
||||||
|
'lastGistSync': Date.now()
|
||||||
|
'lastGistState': 'fail: ' + e
|
||||||
|
})
|
||||||
|
resolve({changes: {}})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
getAll = (syncStore) ->
|
||||||
|
idbKeyval.entries(syncStore).then((entries) ->
|
||||||
|
data = {}
|
||||||
|
entries.forEach((entry) ->
|
||||||
|
data[entry[0]] = entry[1]
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
)
|
||||||
|
|
||||||
|
_processPush = ->
|
||||||
|
if processPush.sequence.length > 0
|
||||||
|
# syncStore = processPush.sequence.shift()
|
||||||
|
syncStore = processPush.sequence[processPush.sequence.length - 1]
|
||||||
|
processPush.sequence.length = 0
|
||||||
|
getAll(syncStore).then((data) ->
|
||||||
|
updateGist(gistId, data)
|
||||||
|
).then( ->
|
||||||
|
_processPush()
|
||||||
|
)
|
||||||
|
else
|
||||||
|
isPushing = false
|
||||||
|
|
||||||
|
processPush = (syncStore) ->
|
||||||
|
processPush.sequence.push(syncStore)
|
||||||
|
return if isPushing
|
||||||
|
isPushing = true
|
||||||
|
_processPush()
|
||||||
|
|
||||||
|
processPush.sequence = []
|
||||||
|
|
||||||
|
getLastCommit = (gistId) ->
|
||||||
|
fetch(gistHost + '/gists/' + gistId + '/commits?per_page=1', {
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/vnd.github+json"
|
||||||
|
"Authorization": "Bearer " + gistToken
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28"
|
||||||
|
}
|
||||||
|
}).then((res) -> res.json()).then((data) ->
|
||||||
|
if data.message
|
||||||
|
throw data.message
|
||||||
|
return data[0]?.version
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
getGist = (gistId) ->
|
||||||
|
#curl -L \
|
||||||
|
# -H "Accept: application/vnd.github+json" \
|
||||||
|
# -H "Authorization: Bearer <YOUR-TOKEN>" \
|
||||||
|
# -H "X-GitHub-Api-Version: 2022-11-28" \
|
||||||
|
# https://api.github.com/gists/GIST_ID
|
||||||
|
fetch(gistHost + '/gists/' + gistId, {
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/vnd.github+json"
|
||||||
|
"Authorization": "Bearer " + gistToken
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28"
|
||||||
|
}
|
||||||
|
}).then((res) -> res.json()).then((data) ->
|
||||||
|
if data.message
|
||||||
|
throw data.message
|
||||||
|
return data
|
||||||
|
)
|
||||||
|
|
||||||
|
updateGist = (gistId, options) ->
|
||||||
|
postBody = {
|
||||||
|
description: 'ZeroOmega Sync'
|
||||||
|
files: {}
|
||||||
|
}
|
||||||
|
postBody.files[optionFilename] = {
|
||||||
|
content: JSON.stringify(options, null, 4)
|
||||||
|
}
|
||||||
|
fetch(gistHost + '/gists/' + gistId, {
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/vnd.github+json"
|
||||||
|
"Authorization": "Bearer " + gistToken
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28"
|
||||||
|
}
|
||||||
|
"method": "PATCH"
|
||||||
|
body: JSON.stringify(postBody)
|
||||||
|
}).then((res) ->
|
||||||
|
res.json()
|
||||||
|
).then((data) ->
|
||||||
|
if data.message
|
||||||
|
throw data.message
|
||||||
|
state?.set({
|
||||||
|
'lastGistCommit': data.history[0]?.version
|
||||||
|
'lastGistState': 'success'
|
||||||
|
'lastGistSync': Date.now()
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
).catch((e) ->
|
||||||
|
state?.set({
|
||||||
|
'lastGistState': 'fail: ' + e
|
||||||
|
'lastGistSync': Date.now()
|
||||||
|
})
|
||||||
|
console.error('update gist fail::', e)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ChromeSyncStorage extends OmegaTarget.Storage
|
||||||
|
@parseStorageErrors: (err) ->
|
||||||
|
if err?.message
|
||||||
|
sustainedPerMinute = 'MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE'
|
||||||
|
if err.message.indexOf('QUOTA_BYTES_PER_ITEM') >= 0
|
||||||
|
err = new OmegaTarget.Storage.QuotaExceededError()
|
||||||
|
err.perItem = true
|
||||||
|
else if err.message.indexOf('QUOTA_BYTES') >= 0
|
||||||
|
err = new OmegaTarget.Storage.QuotaExceededError()
|
||||||
|
else if err.message.indexOf('MAX_ITEMS') >= 0
|
||||||
|
err = new OmegaTarget.Storage.QuotaExceededError()
|
||||||
|
err.maxItems = true
|
||||||
|
else if err.message.indexOf('MAX_WRITE_OPERATIONS_') >= 0
|
||||||
|
err = new OmegaTarget.Storage.RateLimitExceededError()
|
||||||
|
if err.message.indexOf('MAX_WRITE_OPERATIONS_PER_HOUR') >= 0
|
||||||
|
err.perHour = true
|
||||||
|
else if err.message.indexOf('MAX_WRITE_OPERATIONS_PER_MINUTE') >= 0
|
||||||
|
err.perMinute = true
|
||||||
|
else if err.message.indexOf(sustainedPerMinute) >= 0
|
||||||
|
err = new OmegaTarget.Storage.RateLimitExceededError()
|
||||||
|
err.perMinute = true
|
||||||
|
err.sustained = 10
|
||||||
|
else if err.message.indexOf('is not available') >= 0
|
||||||
|
# This could happen if the storage area is not available. For example,
|
||||||
|
# some Chromium-based browsers disable access to the sync storage.
|
||||||
|
err = new OmegaTarget.Storage.StorageUnavailableError()
|
||||||
|
else if err.message.indexOf(
|
||||||
|
'Please set webextensions.storage.sync.enabled to true') >= 0
|
||||||
|
# This happens when sync storage is disabled in flags.
|
||||||
|
err = new OmegaTarget.Storage.StorageUnavailableError()
|
||||||
|
|
||||||
|
return Promise.reject(err)
|
||||||
|
|
||||||
|
constructor: (@areaName, _state) ->
|
||||||
|
state = _state
|
||||||
|
syncStore = idbKeyval.createStore('sync-store', 'sync')
|
||||||
|
@syncStore = syncStore
|
||||||
|
get = (key) ->
|
||||||
|
return new Promise((resolve, reject) ->
|
||||||
|
getAll(syncStore).then((data) ->
|
||||||
|
result = {}
|
||||||
|
if Array.isArray(key)
|
||||||
|
key.forEach( _key ->
|
||||||
|
result[_key] = data[_key]
|
||||||
|
)
|
||||||
|
else if key is null
|
||||||
|
result = data
|
||||||
|
else
|
||||||
|
result[key] = data[key]
|
||||||
|
resolve(result)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
set = (record) ->
|
||||||
|
return new Promise((resolve, reject) ->
|
||||||
|
try
|
||||||
|
if !record or typeof record isnt 'object' or Array.isArray(record)
|
||||||
|
throw new SyntaxError(
|
||||||
|
'Only Object with key value pairs are acceptable')
|
||||||
|
entries = []
|
||||||
|
for own key, value of record
|
||||||
|
entries.push([key, value])
|
||||||
|
idbKeyval.setMany(entries, syncStore).then( ->
|
||||||
|
processPush(syncStore)
|
||||||
|
resolve(record)
|
||||||
|
)
|
||||||
|
catch e
|
||||||
|
reject(e)
|
||||||
|
)
|
||||||
|
_remove = (key) ->
|
||||||
|
if Array.isArray(key)
|
||||||
|
Promise.resolve(idbKeyval.delMany(key, syncStore))
|
||||||
|
else
|
||||||
|
Promise.resolve(idbKeyval.del(key, syncStore))
|
||||||
|
remove = (key) ->
|
||||||
|
Promise.resolve(_remove(key).then( ->
|
||||||
|
processPush(syncStore)
|
||||||
|
return
|
||||||
|
))
|
||||||
|
clear = ->
|
||||||
|
Promise.resolve(idbKeyval.clear(syncStore).then(->
|
||||||
|
processPush(syncStore)
|
||||||
|
return
|
||||||
|
))
|
||||||
|
@storage =
|
||||||
|
get: get
|
||||||
|
set: set
|
||||||
|
remove: remove
|
||||||
|
clear: clear
|
||||||
|
get: (keys) ->
|
||||||
|
keys ?= null
|
||||||
|
Promise.resolve(@storage.get(keys))
|
||||||
|
.catch(ChromeSyncStorage.parseStorageErrors)
|
||||||
|
|
||||||
|
set: (items) ->
|
||||||
|
if Object.keys(items).length == 0
|
||||||
|
return Promise.resolve({})
|
||||||
|
Promise.resolve(@storage.set(items))
|
||||||
|
.catch(ChromeSyncStorage.parseStorageErrors)
|
||||||
|
|
||||||
|
remove: (keys) ->
|
||||||
|
if not keys?
|
||||||
|
return Promise.resolve(@storage.clear())
|
||||||
|
if Array.isArray(keys) and keys.length == 0
|
||||||
|
return Promise.resolve({})
|
||||||
|
Promise.resolve(@storage.remove(keys))
|
||||||
|
.catch(ChromeSyncStorage.parseStorageErrors)
|
||||||
|
flush: ({data}) ->
|
||||||
|
entries = []
|
||||||
|
result = null
|
||||||
|
if data and data.schemaVersion
|
||||||
|
for own key, value of data
|
||||||
|
entries.push([key, value])
|
||||||
|
result = idbKeyval.setMany(entries, @syncStore)
|
||||||
|
Promise.resolve(result)
|
||||||
|
|
||||||
|
##
|
||||||
|
# param(withRemoteData) retrive gist file content
|
||||||
|
##
|
||||||
|
init: (args) ->
|
||||||
|
gistId = args.gistId
|
||||||
|
gistToken = args.gistToken
|
||||||
|
return new Promise((resolve, reject) ->
|
||||||
|
getLastCommit(gistId).then( (lastGistCommit) ->
|
||||||
|
if args.withRemoteData
|
||||||
|
getGist(gistId).then((gist) ->
|
||||||
|
try
|
||||||
|
optionsStr = gist.files[optionFilename].content
|
||||||
|
options = JSON.parse(optionsStr)
|
||||||
|
resolve({options, lastGistCommit})
|
||||||
|
catch e
|
||||||
|
resolve({})
|
||||||
|
)
|
||||||
|
else
|
||||||
|
resolve({})
|
||||||
|
).catch((e) ->
|
||||||
|
reject(e)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
##
|
||||||
|
# param (opts) opts.immediately , immediately update changed
|
||||||
|
# param (opts) opts.force, force get remote content
|
||||||
|
##
|
||||||
|
checkChange: (opts = {}) ->
|
||||||
|
isPulling = true
|
||||||
|
processCheckCommit().then((isChanged) =>
|
||||||
|
if isChanged or opts.force
|
||||||
|
processPull(@syncStore).then(({changes, remoteOptions}) =>
|
||||||
|
@flush({data: remoteOptions}).then( =>
|
||||||
|
isPulling = false
|
||||||
|
ChromeSyncStorage.onChangedListener(changes, @areaName, opts)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
console.log('no changed')
|
||||||
|
isPulling = false
|
||||||
|
)
|
||||||
|
|
||||||
|
watch: (keys, callback) ->
|
||||||
|
chrome.alarms.create('omega.syncCheck', {
|
||||||
|
periodInMinutes: 5
|
||||||
|
})
|
||||||
|
ChromeSyncStorage.watchers[@areaName] ?= {}
|
||||||
|
area = ChromeSyncStorage.watchers[@areaName]
|
||||||
|
watcher = {keys: keys, callback: callback}
|
||||||
|
enableSync = true
|
||||||
|
id = Date.now().toString()
|
||||||
|
while area[id]
|
||||||
|
id = Date.now().toString()
|
||||||
|
|
||||||
|
if Array.isArray(keys)
|
||||||
|
keyMap = {}
|
||||||
|
for key in keys
|
||||||
|
keyMap[key] = true
|
||||||
|
keys = keyMap
|
||||||
|
area[id] = {keys: keys, callback: callback}
|
||||||
|
if not onChangedListenerInstalled
|
||||||
|
# chrome alerm
|
||||||
|
@checkChange()
|
||||||
|
chrome.alarms.onAlarm.addListener (alarm) =>
|
||||||
|
return unless enableSync
|
||||||
|
return if isPulling
|
||||||
|
switch alarm.name
|
||||||
|
when 'omega.syncCheck'
|
||||||
|
@checkChange()
|
||||||
|
#chrome.storage.onChanged.addListener(ChromeSyncStorage.onChangedListener)
|
||||||
|
onChangedListenerInstalled = true
|
||||||
|
return ->
|
||||||
|
enableSync = false
|
||||||
|
delete area[id]
|
||||||
|
|
||||||
|
##
|
||||||
|
# param (opts) opts.immediately , immediately update changed
|
||||||
|
##
|
||||||
|
@onChangedListener: (changes, areaName, opts = {}) ->
|
||||||
|
map = null
|
||||||
|
for _, watcher of ChromeSyncStorage.watchers[areaName]
|
||||||
|
match = watcher.keys == null
|
||||||
|
if not match
|
||||||
|
for own key of changes
|
||||||
|
if watcher.keys[key]
|
||||||
|
match = true
|
||||||
|
break
|
||||||
|
if match
|
||||||
|
if not map?
|
||||||
|
map = {}
|
||||||
|
for own key, change of changes
|
||||||
|
map[key] = change.newValue
|
||||||
|
watcher.callback(map, opts)
|
||||||
|
|
||||||
|
@watchers: {}
|
||||||
|
|
||||||
|
module.exports = ChromeSyncStorage
|
@ -59,9 +59,9 @@ class BrowserStorage extends Storage
|
|||||||
else
|
else
|
||||||
index = 0
|
index = 0
|
||||||
while true
|
while true
|
||||||
key = @proto.key.call(index)
|
key = @proto.key.call(@storage, index)
|
||||||
break if key == null
|
break if key == null
|
||||||
if @key.substr(0, @prefix.length) == @prefix
|
if key.substr(0, @prefix.length) == @prefix
|
||||||
@proto.removeItem.call(@storage, @prefix + keys)
|
@proto.removeItem.call(@storage, @prefix + keys)
|
||||||
else
|
else
|
||||||
index++
|
index++
|
||||||
|
@ -4,7 +4,7 @@ Log = require './log'
|
|||||||
replacer = (key, value) ->
|
replacer = (key, value) ->
|
||||||
switch key
|
switch key
|
||||||
# Hide values for a few keys with privacy concerns.
|
# Hide values for a few keys with privacy concerns.
|
||||||
when "username", "password", "host", "port"
|
when "username", "password", "host", "port", "token", "gistToken", "gistId"
|
||||||
return "<secret>"
|
return "<secret>"
|
||||||
else
|
else
|
||||||
value
|
value
|
||||||
|
@ -45,6 +45,8 @@ class Options
|
|||||||
# @returns {{}} The transformed value
|
# @returns {{}} The transformed value
|
||||||
###
|
###
|
||||||
@transformValueForSync: (value, key) ->
|
@transformValueForSync: (value, key) ->
|
||||||
|
if key is '-customCss'
|
||||||
|
return undefined
|
||||||
if key[0] == '+'
|
if key[0] == '+'
|
||||||
if OmegaPac.Profiles.updateUrl(value)
|
if OmegaPac.Profiles.updateUrl(value)
|
||||||
profile = {}
|
profile = {}
|
||||||
@ -83,22 +85,36 @@ class Options
|
|||||||
@_watchStop = null
|
@_watchStop = null
|
||||||
|
|
||||||
loadRaw = if options? then Promise.resolve(options) else
|
loadRaw = if options? then Promise.resolve(options) else
|
||||||
if not @sync?.enabled
|
if not @sync?
|
||||||
if not @sync?
|
@_state.set({'syncOptions': 'unsupported'})
|
||||||
@_state.set({'syncOptions': 'unsupported'})
|
|
||||||
@_storage.get(null)
|
@_storage.get(null)
|
||||||
else
|
else
|
||||||
@_state.set({'syncOptions': 'sync'})
|
@_state.get({
|
||||||
@_syncWatchStop = @sync.watchAndPull(@_storage)
|
'syncOptions': ''
|
||||||
@sync.copyTo(@_storage).catch(Storage.StorageUnavailableError, =>
|
'gistId': ''
|
||||||
console.error('Warning: Sync storage is not available in this ' +
|
'gistToken': ''
|
||||||
'browser! Disabling options sync.')
|
}).then(({syncOptions, gistId, gistToken}) =>
|
||||||
@_syncWatchStop?()
|
unless gistId
|
||||||
@_syncWatchStop = null
|
syncOptions = 'pristine'
|
||||||
@sync = null
|
@_state.set({'syncOptions': 'pristine'})
|
||||||
@_state.set({'syncOptions': 'unsupported'})
|
@sync.enabled = syncOptions is 'sync'
|
||||||
).then =>
|
unless @sync.enabled
|
||||||
@_storage.get(null)
|
@_storage.get(null)
|
||||||
|
else
|
||||||
|
@sync.init({gistId, gistToken}).catch((e) ->
|
||||||
|
console.error('sync init fail::', e)
|
||||||
|
)
|
||||||
|
@_syncWatchStop = @sync.watchAndPull(@_storage)
|
||||||
|
@sync.copyTo(@_storage).catch(Storage.StorageUnavailableError, =>
|
||||||
|
console.error('Warning: Sync storage is not available in this ' +
|
||||||
|
'browser! Disabling options sync.')
|
||||||
|
@_syncWatchStop?()
|
||||||
|
@_syncWatchStop = null
|
||||||
|
@sync = null
|
||||||
|
@_state.set({'syncOptions': 'unsupported'})
|
||||||
|
).then =>
|
||||||
|
@_storage.get(null)
|
||||||
|
)
|
||||||
|
|
||||||
@optionsLoaded = loadRaw.then((options) =>
|
@optionsLoaded = loadRaw.then((options) =>
|
||||||
@upgrade(options)
|
@upgrade(options)
|
||||||
@ -235,7 +251,7 @@ class Options
|
|||||||
# Current schemaVersion.
|
# Current schemaVersion.
|
||||||
Promise.resolve([options, changes])
|
Promise.resolve([options, changes])
|
||||||
else
|
else
|
||||||
Promise.reject new Error("Invalid schemaVerion #{version}!")
|
Promise.reject new Error("Invalid schemaVersion #{version}!")
|
||||||
|
|
||||||
###*
|
###*
|
||||||
# Parse options in various formats (including JSON & base64).
|
# Parse options in various formats (including JSON & base64).
|
||||||
@ -1004,53 +1020,80 @@ class Options
|
|||||||
# @param {boolean=false} args.force If true, overwrite options when conflict
|
# @param {boolean=false} args.force If true, overwrite options when conflict
|
||||||
# @returns {Promise} A promise which is fulfilled when the syncing is switched
|
# @returns {Promise} A promise which is fulfilled when the syncing is switched
|
||||||
###
|
###
|
||||||
setOptionsSync: (enabled, args) ->
|
setOptionsSync: (enabled, args = {}) ->
|
||||||
@log.method('Options#setOptionsSync', this, arguments)
|
@log.method('Options#setOptionsSync', this, arguments)
|
||||||
if not @sync?
|
if not @sync?
|
||||||
return Promise.reject(new Error('Options syncing is unsupported.'))
|
return Promise.reject(new Error('Options syncing is unsupported.'))
|
||||||
@_state.get({'syncOptions': ''}).then ({syncOptions}) =>
|
@_state.get({
|
||||||
|
'syncOptions': '', lastGistCommit: ''
|
||||||
|
}).then ({syncOptions, lastGistCommit}) =>
|
||||||
if not enabled
|
if not enabled
|
||||||
if syncOptions == 'sync'
|
if syncOptions == 'sync'
|
||||||
@_state.set({'syncOptions': 'conflict'})
|
@_state.set({'syncOptions': 'pristine'})
|
||||||
@sync.enabled = false
|
@sync.enabled = false
|
||||||
@_syncWatchStop?()
|
@_syncWatchStop?()
|
||||||
@_syncWatchStop = null
|
@_syncWatchStop = null
|
||||||
return
|
return
|
||||||
|
|
||||||
if syncOptions == 'conflict'
|
if syncOptions == 'conflict'
|
||||||
if not args?.force
|
if not args.force
|
||||||
return Promise.reject(new Error(
|
return Promise.reject(new Error(
|
||||||
'Syncing not enabled due to conflict. Retry with force to overwrite
|
'Syncing not enabled due to conflict. Retry with force to overwrite
|
||||||
local options and enable syncing.'))
|
local options and enable syncing.'))
|
||||||
return if syncOptions == 'sync'
|
return if syncOptions == 'sync'
|
||||||
@_state.set({'syncOptions': 'sync'}).then =>
|
{ gistId, gistToken } = args
|
||||||
if syncOptions == 'conflict'
|
@sync.init({
|
||||||
# Try to re-init options from sync.
|
gistId, gistToken, withRemoteData: true
|
||||||
@sync.enabled = false
|
}).then( ({
|
||||||
@_storage.remove().then =>
|
options: remoteOptions, lastGistCommit: remoteLastGistCommit
|
||||||
@sync.enabled = true
|
}) =>
|
||||||
@init()
|
@_state.set({
|
||||||
else
|
'syncOptions': 'sync'
|
||||||
@sync.enabled = true
|
'gistId': gistId
|
||||||
@_syncWatchStop?()
|
'gistToken': gistToken
|
||||||
@sync.requestPush(@_options)
|
}).then =>
|
||||||
@_syncWatchStop = @sync.watchAndPull(@_storage)
|
if syncOptions == 'conflict'
|
||||||
return
|
# Try to re-init options from sync.
|
||||||
|
@sync.enabled = false
|
||||||
|
@_storage.remove().then =>
|
||||||
|
@sync.enabled = true
|
||||||
|
@init()
|
||||||
|
else
|
||||||
|
if remoteOptions.schemaVersion
|
||||||
|
@sync.flush({data: remoteOptions}).then( =>
|
||||||
|
@sync.enabled = false
|
||||||
|
@_state.set({'syncOptions': 'conflict'})
|
||||||
|
return
|
||||||
|
)
|
||||||
|
else
|
||||||
|
@sync.enabled = true
|
||||||
|
@_syncWatchStop?()
|
||||||
|
@sync.requestPush(@_options)
|
||||||
|
@_syncWatchStop = @sync.watchAndPull(@_storage)
|
||||||
|
return
|
||||||
|
)
|
||||||
|
|
||||||
###*
|
###*
|
||||||
# Clear the sync storage, resetting syncing state to pristine.
|
# Clear the sync storage, resetting syncing state to pristine.
|
||||||
# @returns {Promise} A promise which is fulfilled when the syncing is reset.
|
# @returns {Promise} A promise which is fulfilled when the syncing is reset.
|
||||||
###
|
###
|
||||||
resetOptionsSync: ->
|
resetOptionsSync: (args) ->
|
||||||
@log.method('Options#resetOptionsSync', this, arguments)
|
@log.method('Options#resetOptionsSync', this, arguments)
|
||||||
if not @sync?
|
if not @sync?
|
||||||
return Promise.reject(new Error('Options syncing is unsupported.'))
|
return Promise.reject(new Error('Options syncing is unsupported.'))
|
||||||
@sync.enabled = false
|
@sync.enabled = false
|
||||||
@_syncWatchStop?()
|
@_syncWatchStop?()
|
||||||
@_syncWatchStop = null
|
@_syncWatchStop = null
|
||||||
@_state.set({'syncOptions': 'conflict'})
|
@_state.set({'syncOptions': 'conflict'}).then( =>
|
||||||
|
@sync.init(args)
|
||||||
return @sync.storage.remove().then =>
|
).then( =>
|
||||||
|
@sync.storage.remove()
|
||||||
|
).then( =>
|
||||||
@_state.set({'syncOptions': 'pristine'})
|
@_state.set({'syncOptions': 'pristine'})
|
||||||
|
)
|
||||||
|
|
||||||
|
checkOptionsSyncChange: ->
|
||||||
|
if @sync and @sync.enabled
|
||||||
|
@sync.checkChange()
|
||||||
|
|
||||||
module.exports = Options
|
module.exports = Options
|
||||||
|
@ -205,10 +205,22 @@ class OptionsSync
|
|||||||
@_logOperations('OptionsSync::pull', operations)
|
@_logOperations('OptionsSync::pull', operations)
|
||||||
local.apply(operations)
|
local.apply(operations)
|
||||||
|
|
||||||
@storage.watch null, (changes) =>
|
@storage.watch null, (changes, opts = {}) =>
|
||||||
for own key, value of changes
|
for own key, value of changes
|
||||||
pull[key] = value
|
pull[key] = value
|
||||||
return if pullScheduled?
|
return if pullScheduled?
|
||||||
pullScheduled = setTimeout(doPull, @pullThrottle)
|
if opts.immediately
|
||||||
|
doPull()
|
||||||
|
else
|
||||||
|
pullScheduled = setTimeout(doPull, @pullThrottle)
|
||||||
|
checkChange: ->
|
||||||
|
@storage.checkChange({
|
||||||
|
immediately: true
|
||||||
|
force: true
|
||||||
|
})
|
||||||
|
init: (args) ->
|
||||||
|
@storage.init(args)
|
||||||
|
flush: ({data}) ->
|
||||||
|
@storage.flush({data})
|
||||||
|
|
||||||
module.exports = OptionsSync
|
module.exports = OptionsSync
|
||||||
|
@ -172,7 +172,7 @@ fieldset[disabled] .form-control {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.alert-success {
|
.alert-success {
|
||||||
color: var(--primaryColor);
|
color: var(--positiveColor);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-color: var(--lighterBackground);
|
border-color: var(--lighterBackground);
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -185,7 +185,7 @@ fieldset[disabled] .form-control {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
background-color: var(--primaryColor);
|
background-color: var(--positiveColor);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,3 +429,9 @@ main .page-header {
|
|||||||
.sp-palette-container {
|
.sp-palette-container {
|
||||||
border-right-color: var(--selectionBackground);
|
border-right-color: var(--selectionBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-group-addon{
|
||||||
|
color: var(--defaultForeground);
|
||||||
|
background-color: var(--lighterBackground);
|
||||||
|
border-color: var(--lighterBackground);
|
||||||
|
}
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
angular.module('omega').controller 'AboutCtrl', ($scope, $rootScope,
|
angular.module('omega').controller 'AboutCtrl', (
|
||||||
$modal, omegaDebug) ->
|
$scope, $rootScope,$modal, omegaDebug
|
||||||
|
) ->
|
||||||
$scope.downloadLog = omegaDebug.downloadLog
|
$scope.downloadLog = ->
|
||||||
$scope.reportIssue = omegaDebug.reportIssue
|
$scope.logDownloading = true
|
||||||
|
Promise.resolve(omegaDebug.downloadLog()).then( ->
|
||||||
|
$scope.logDownloading = false
|
||||||
|
)
|
||||||
|
$scope.reportIssue = ->
|
||||||
|
$scope.issueReporting = true
|
||||||
|
omegaDebug.reportIssue().then( ->
|
||||||
|
$scope.issueReporting = false
|
||||||
|
)
|
||||||
|
|
||||||
$scope.showResetOptionsModal = ->
|
$scope.showResetOptionsModal = ->
|
||||||
$modal.open(templateUrl: 'partials/reset_options_confirm.html').result
|
$modal
|
||||||
.then -> omegaDebug.resetOptions()
|
.open(templateUrl: 'partials/reset_options_confirm.html').result
|
||||||
|
.then ->
|
||||||
|
$scope.optionsReseting = true
|
||||||
|
omegaDebug.resetOptions().then( ->
|
||||||
|
$scope.optionsReseting = false
|
||||||
|
)
|
||||||
|
|
||||||
try
|
try
|
||||||
$scope.version = omegaDebug.getProjectVersion()
|
$scope.version = omegaDebug.getProjectVersion()
|
||||||
|
@ -1,9 +1,22 @@
|
|||||||
angular.module('omega').controller 'IoCtrl', ($scope, $rootScope,
|
angular.module('omega').controller 'IoCtrl', (
|
||||||
$window, $http, omegaTarget, downloadFile) ->
|
$scope, $rootScope, $window, $http, omegaTarget, downloadFile
|
||||||
|
) ->
|
||||||
|
|
||||||
omegaTarget.state('web.restoreOnlineUrl').then (url) ->
|
omegaTarget.state([
|
||||||
|
'web.restoreOnlineUrl',
|
||||||
|
'gistId',
|
||||||
|
'gistToken',
|
||||||
|
'lastGistSync',
|
||||||
|
'lastGistState'
|
||||||
|
]).then ([url, gistId, gistToken, lastGistSync, lastGistState]) ->
|
||||||
if url
|
if url
|
||||||
$scope.restoreOnlineUrl = url
|
$scope.restoreOnlineUrl = url
|
||||||
|
if gistId
|
||||||
|
$scope.gistId = gistId
|
||||||
|
if gistToken
|
||||||
|
$scope.gistToken = gistToken
|
||||||
|
$scope.lastGistSync = new Date(lastGistSync or Date.now())
|
||||||
|
$scope.lastGistState = lastGistState or ''
|
||||||
|
|
||||||
$scope.exportOptions = ->
|
$scope.exportOptions = ->
|
||||||
$rootScope.applyOptionsConfirm().then ->
|
$rootScope.applyOptionsConfirm().then ->
|
||||||
@ -57,21 +70,59 @@ angular.module('omega').controller 'IoCtrl', ($scope, $rootScope,
|
|||||||
), $scope.downloadError).finally ->
|
), $scope.downloadError).finally ->
|
||||||
$scope.restoringOnline = false
|
$scope.restoringOnline = false
|
||||||
|
|
||||||
$scope.enableOptionsSync = (args) ->
|
$scope.enableOptionsSync = (args = {}) ->
|
||||||
enable = ->
|
enable = ->
|
||||||
omegaTarget.setOptionsSync(true, args).finally ->
|
if !$scope.gistId or !$scope.gistToken
|
||||||
|
$rootScope.showAlert(
|
||||||
|
type: 'error'
|
||||||
|
message: 'Gist Id or Gist Token is required'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
args.gistId = $scope.gistId
|
||||||
|
args.gistToken = $scope.gistToken
|
||||||
|
$scope.enableOptionsSyncing = true
|
||||||
|
omegaTarget.setOptionsSync(true, args).then( ->
|
||||||
$window.location.reload()
|
$window.location.reload()
|
||||||
|
).catch((e) ->
|
||||||
|
$scope.enableOptionsSyncing = false
|
||||||
|
$rootScope.showAlert(
|
||||||
|
type: 'error'
|
||||||
|
message: e + ''
|
||||||
|
)
|
||||||
|
console.log('error:::', e)
|
||||||
|
)
|
||||||
if args?.force
|
if args?.force
|
||||||
enable()
|
enable()
|
||||||
else
|
else
|
||||||
$rootScope.applyOptionsConfirm().then enable
|
$rootScope.applyOptionsConfirm().then enable
|
||||||
|
|
||||||
|
$scope.checkOptionsSyncChange = ->
|
||||||
|
$scope.enableOptionsSyncing = true
|
||||||
|
omegaTarget.checkOptionsSyncChange().then( ->
|
||||||
|
$window.location.reload()
|
||||||
|
)
|
||||||
$scope.disableOptionsSync = ->
|
$scope.disableOptionsSync = ->
|
||||||
omegaTarget.setOptionsSync(false).then ->
|
omegaTarget.setOptionsSync(false).then ->
|
||||||
$rootScope.applyOptionsConfirm().then ->
|
$rootScope.applyOptionsConfirm().then ->
|
||||||
$window.location.reload()
|
$window.location.reload()
|
||||||
|
|
||||||
$scope.resetOptionsSync = ->
|
$scope.resetOptionsSync = ->
|
||||||
omegaTarget.resetOptionsSync().then ->
|
if !$scope.gistId or !$scope.gistToken
|
||||||
|
$rootScope.showAlert(
|
||||||
|
type: 'error'
|
||||||
|
message: 'Gist Id or Gist Token is required'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
omegaTarget.resetOptionsSync({
|
||||||
|
gistId: $scope.gistId
|
||||||
|
gistToken: $scope.gistToken
|
||||||
|
}).then( ->
|
||||||
$rootScope.applyOptionsConfirm().then ->
|
$rootScope.applyOptionsConfirm().then ->
|
||||||
$window.location.reload()
|
$window.location.reload()
|
||||||
|
).catch((e) ->
|
||||||
|
$rootScope.showAlert(
|
||||||
|
type: 'error'
|
||||||
|
message: e + ''
|
||||||
|
)
|
||||||
|
console.log('error:::', e)
|
||||||
|
)
|
||||||
|
@ -90,9 +90,6 @@ angular.module('omega').controller 'MasterCtrl', ($scope, $rootScope, $window,
|
|||||||
plainOptions = angular.fromJson(angular.toJson($rootScope.options))
|
plainOptions = angular.fromJson(angular.toJson($rootScope.options))
|
||||||
patch = diff.diff($rootScope.optionsOld, plainOptions)
|
patch = diff.diff($rootScope.optionsOld, plainOptions)
|
||||||
omegaTarget.optionsPatch(patch).then ->
|
omegaTarget.optionsPatch(patch).then ->
|
||||||
|
|
||||||
omegaTarget.state('customCss').then (customCss = '') ->
|
|
||||||
$scope.customCss = customCss
|
|
||||||
$rootScope.showAlert(
|
$rootScope.showAlert(
|
||||||
type: 'success'
|
type: 'success'
|
||||||
i18n: 'options_saveSuccess'
|
i18n: 'options_saveSuccess'
|
||||||
|
@ -14,17 +14,17 @@ section
|
|||||||
p {{'about_app_description' | tr}}
|
p {{'about_app_description' | tr}}
|
||||||
section
|
section
|
||||||
p
|
p
|
||||||
button.btn.btn-info(ng-click='reportIssue()')
|
button.btn.btn-info(ng-click='reportIssue()' ladda='issueReporting')
|
||||||
span.glyphicon.glyphicon-comment
|
span.glyphicon.glyphicon-comment
|
||||||
= ' '
|
= ' '
|
||||||
| {{'popup_reportIssues' | tr}}
|
| {{'popup_reportIssues' | tr}}
|
||||||
= ' '
|
= ' '
|
||||||
button.btn.btn-default(ng-click='downloadLog()')
|
button.btn.btn-default(ng-click='downloadLog()' ladda='logDownloading' data-spinner-color="currentColor")
|
||||||
span.glyphicon.glyphicon-download
|
span.glyphicon.glyphicon-download
|
||||||
= ' '
|
= ' '
|
||||||
| {{'popup_errorLog' | tr}}
|
| {{'popup_errorLog' | tr}}
|
||||||
= ' '
|
= ' '
|
||||||
button.btn.btn-danger(ng-click='showResetOptionsModal()')
|
button.btn.btn-danger(ng-click='showResetOptionsModal()' ladda='optionsReseting')
|
||||||
span.glyphicon.glyphicon-alert
|
span.glyphicon.glyphicon-alert
|
||||||
= ' '
|
= ' '
|
||||||
| {{'options_reset' | tr}}
|
| {{'options_reset' | tr}}
|
||||||
|
@ -38,15 +38,47 @@ section.settings-group
|
|||||||
| {{'options_restoreOnlineSubmit' | tr}}
|
| {{'options_restoreOnlineSubmit' | tr}}
|
||||||
section.settings-group
|
section.settings-group
|
||||||
h3 {{'options_group_syncing' | tr}}
|
h3 {{'options_group_syncing' | tr}}
|
||||||
|
div
|
||||||
|
form
|
||||||
|
div.form-group
|
||||||
|
label {{'Gist Id'}}
|
||||||
|
.input-group.width-limit
|
||||||
|
span.input-group-addon {{'ID'}}
|
||||||
|
input.form-control(type='text' ng-model='gistId' ng-readonly='syncOptions == "sync"' placeholder="Gist Id e.g. https://gist.github.com/{username}/{Gist Id}")
|
||||||
|
span.help-block
|
||||||
|
a(href="https://gist.github.com/" role="button" target="_blank")
|
||||||
|
| {{'Create a secret Gist. '}}
|
||||||
|
strong
|
||||||
|
| {{" Note: If it's a public Gist, your options can be searched by others。"}}
|
||||||
|
div.form-group
|
||||||
|
label {{'Gist Token'}}
|
||||||
|
.input-group.width-limit
|
||||||
|
span.input-group-addon {{'TOKEN'}}
|
||||||
|
input.form-control(type='text' ng-model='gistToken' ng-readonly='syncOptions == "sync"' placeholder="Gist Token")
|
||||||
|
span.help-block
|
||||||
|
a(href="https://github.com/settings/tokens/new" role="button" target="_blank")
|
||||||
|
| {{ 'Create a token that manages the Gist.'}}
|
||||||
div(ng-show='syncOptions == "pristine" || syncOptions == "disabled"')
|
div(ng-show='syncOptions == "pristine" || syncOptions == "disabled"')
|
||||||
p.help-block(omega-html='"options_syncPristineHelp" | tr')
|
p.help-block(omega-html='"options_syncPristineHelp" | tr')
|
||||||
p
|
p
|
||||||
button.btn.btn-default(ng-click='enableOptionsSync()')
|
button.btn.btn-default(ng-click='enableOptionsSync()' ladda='enableOptionsSyncing' data-spinner-color="currentColor")
|
||||||
span.glyphicon.glyphicon-cloud-upload
|
span.glyphicon.glyphicon-cloud-upload
|
||||||
= ' '
|
= ' '
|
||||||
| {{'options_syncEnable' | tr}}
|
| {{'options_syncEnable' | tr}}
|
||||||
div(ng-show='syncOptions == "sync"')
|
div(ng-show='syncOptions == "sync"')
|
||||||
p.alert.alert-success.width-limit
|
p.alert.alert-success.width-limit
|
||||||
|
button.btn.btn-sm.btn-success(ng-click='checkOptionsSyncChange()' ladda='enableOptionsSyncing')
|
||||||
|
span.glyphicon.glyphicon-refresh
|
||||||
|
span {{' last sync date: '}}
|
||||||
|
| {{ lastGistSync | date:'medium'}}
|
||||||
|
| {{'('}}
|
||||||
|
| {{lastGistState}}
|
||||||
|
| {{')'}}
|
||||||
|
a(href="https://gist.github.com/{{gistId}}" role="button" target="_blank")
|
||||||
|
| {{' '}}
|
||||||
|
span.glyphicon.glyphicon-link
|
||||||
|
br
|
||||||
|
br
|
||||||
span.glyphicon.glyphicon-ok
|
span.glyphicon.glyphicon-ok
|
||||||
= ' '
|
= ' '
|
||||||
| {{"options_syncSyncAlert" | tr}}
|
| {{"options_syncSyncAlert" | tr}}
|
||||||
@ -57,13 +89,13 @@ section.settings-group
|
|||||||
= ' '
|
= ' '
|
||||||
| {{'options_syncDisable' | tr}}
|
| {{'options_syncDisable' | tr}}
|
||||||
div(ng-show='syncOptions == "conflict"')
|
div(ng-show='syncOptions == "conflict"')
|
||||||
p.alert.alert-info.width-limit
|
p.alert.alert-danger.width-limit
|
||||||
span.glyphicon.glyphicon-info-sign
|
span.glyphicon.glyphicon-info-sign
|
||||||
= ' '
|
= ' '
|
||||||
| {{"options_syncConflictAlert" | tr}}
|
| {{"options_syncConflictAlert" | tr}}
|
||||||
p.help-block(omega-html='"options_syncConflictHelp" | tr')
|
p.help-block(omega-html='"options_syncConflictHelp" | tr')
|
||||||
p
|
p
|
||||||
button.btn.btn-danger(ng-click='enableOptionsSync({force: true})')
|
button.btn.btn-danger(ng-click='enableOptionsSync({force: true})' ladda='enableOptionsSyncing' )
|
||||||
span.glyphicon.glyphicon-cloud-download
|
span.glyphicon.glyphicon-cloud-download
|
||||||
= ' '
|
= ' '
|
||||||
| {{'options_syncEnableForce' | tr}}
|
| {{'options_syncEnableForce' | tr}}
|
||||||
|
Loading…
Reference in New Issue
Block a user