Skip to content
GitLab
Explore
Projects
Groups
Snippets
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Memri
Browser application
Commits
2714a854
Commit
2714a854
authored
4 years ago
by
Amirjanyan
Browse files
Options
Download
Email Patches
Plain Diff
Convert from swift to js
parent
6c217db2
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
context/MemriContext.ts
+510
-0
context/MemriContext.ts
with
510 additions
and
0 deletions
+510
-0
context/MemriContext.ts
0 → 100644
+
510
-
0
View file @
2714a854
/*
Notes on documentation
We use the following documentation keywords
- bug
- Remark
- Requires
- See also
- warning
Also remember, when using markdown in your documentation
- Use backticks for code
*/
// TODO: Remove this and find a solution for Edges
var
globalCache
class
Alias
{
key
:
string
type
:
string
on
?
off
?
constructor
(
key
,
type
,
on
?,
off
?)
{
this
.
key
=
key
this
.
type
=
type
this
.
on
=
on
this
.
off
=
off
}
}
class
MemriContext
extends
ObservableObject
{
name
=
""
/// The current session that is active in the application
currentSession
:
Session
?
cascadingView
:
CascadingView
?
sessions
:
Sessions
?
views
:
Views
settings
:
Settings
installer
:
Installer
podAPI
:
PodAPI
indexerAPI
:
IndexerAPI
cache
:
Cache
realm
:
Realm
navigation
:
MainNavigation
renderers
:
Renderers
get
items
(){
return
this
.
cascadingView
?.
resultSet
.
items
??
[]
}
set
items
(
items
)
{
// Do nothing
console
.
log
(
"
THIS SHOULD NEVER BE PRINTED2
"
)
}
get
item
()
{
return
this
.
cascadingView
?.
resultSet
.
singletonItem
}
set
item
(
item
)
{
// Do nothing
console
.
log
(
"
THIS SHOULD NEVER BE PRINTED
"
)
}
closeStack
=
[]
// A stack of bindings for the display state of presented popups
addToStack
(
isPresentedBinding
)
{
this
.
closeStack
.
push
(
isPresentedBinding
)
}
closeLastInStack
()
{
let
lastVisibleIndex
=
null
//TODO closeStack.lastIndex(where: { $0.wrappedValue.isPresented })
for
(
var
i
=
this
.
closeStack
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
this
.
closeStack
[
i
].
wrappedValue
.
isPresented
)
{
lastVisibleIndex
=
i
break
}
}
if
(
lastVisibleIndex
>=
0
)
{
this
.
closeStack
[
lastVisibleIndex
].
wrappedValue
.
dismiss
()
this
.
closeStack
=
this
.
closeStack
.
slice
(
0
,
lastVisibleIndex
)
}
}
scheduled
=
false
scheduledComputeView
=
false
scheduleUIUpdate
(
immediate
=
false
,
check
?)
{
// Update UI
let
outcome
=
function
()
{
// Reset scheduled
this
.
scheduled
=
false
// Update UI
this
.
objectWillChange
.
send
()
}
if
(
immediate
)
{
// Do this straight away, usually for the sake of correct animation
outcome
()
return
}
if
(
check
)
{
if
(
!
check
(
this
))
{
return
}
}
// Don't schedule when we are already scheduled
if
(
this
.
scheduled
)
{
return
}
// Prevent multiple calls to the dispatch queue
this
.
scheduled
=
true
// Schedule update
/*DispatchQueue.main.async {//TODO
outcome()
}*/
}
scheduleCascadingViewUpdate
(
immediate
=
false
)
{
let
outcome
=
function
()
{
// Reset scheduled
this
.
scheduledComputeView
=
false
// Update UI
try
{
this
.
updateCascadingView
()
}
catch
(
error
)
{
// TODO: User error handling
// TODO: Error Handling
debugHistory
.
error
(
`Could not update CascadingView:
${
error
}
`
)
//TODO
}
}
if
(
immediate
)
{
// Do this straight away, usually for the sake of correct animation
outcome
()
return
}
// Don't schedule when we are already scheduled
if
(
!
this
.
scheduledComputeView
)
{
// Prevent multiple calls to the dispatch queue
this
.
scheduledComputeView
=
true
// Schedule update
/*DispatchQueue.main.async {//TODO
outcome()
}*/
}
}
updateCascadingView
()
{
this
.
maybeLogUpdate
()
let
currentView
=
this
.
sessions
?.
currentView
// Fetch datasource if (not yet parsed yet
if
(
!
currentView
)
{
throw
new
Error
(
"
Exception: currentView is not set
"
)
}
if
(
currentView
.
datasource
==
undefined
)
{
let
parsedDef
=
this
.
views
.
parseDefinition
(
currentView
.
viewDefinition
)
if
(
parsedDef
)
{
let
ds
=
parsedDef
[
"
datasourceDefinition
"
]
if
(
ds
&&
ds
instanceof
CVUParsedDatasourceDefinition
)
{
// TODO: this is at the wrong moment. Should be done after cascading
currentView
.
set
(
"
datasource
"
,
Datasource
.
fromCVUDefinition
(
ds
,
currentView
.
viewArguments
))
}
else
{
throw
new
Error
(
"
Exception: Missing datasource in session view
"
)
}
}
else
{
throw
new
Error
(
"
Exception: Unable to parse view definition
"
)
}
}
let
datasource
=
currentView
.
datasource
if
(
!
datasource
)
{
throw
new
Error
(
"
Exception: Missing datasource in session view
"
)
}
// Fetch the resultset associated with the current view
let
resultSet
=
this
.
cache
.
getResultSet
(
datasource
)
// If we can guess the type of the result based on the query, let's compute the view
if
(
resultSet
.
determinedType
!=
undefined
)
{
if
(
this
instanceof
RootContext
)
{
// if (type(of: self) == RootMain.self) {
debugHistory
.
info
(
"
Computing view
"
//TODO
+
(
currentView
.
name
??
currentView
.
viewDefinition
?.
selector
??
""
))
}
try
{
// Calculate cascaded view
let
cascadingView
=
this
.
views
.
createCascadingView
()
// TODO: handle errors better
// Update current session
this
.
currentSession
=
this
.
sessions
?.
currentSession
// TODO: filter to a single property
// Set the newly cascading view
this
.
cascadingView
=
cascadingView
// Load data in the resultset of the computed view
this
.
cascadingView
?.
resultSet
.
load
(
function
(
error
)
{
if
(
error
)
{
// TODO: Refactor: Log warning to user
console
.
log
(
`Error: could not load result:
${
error
}
`
)
}
else
{
this
.
maybeLogRead
()
// Update the UI
this
.
scheduleUIUpdate
()
}
})
}
catch
(
error
)
{
// TODO: Error handling
// TODO: User Error handling
debugHistory
.
error
(
`
${
error
}
`
)
//TODO
}
// Update the UI
this
.
scheduleUIUpdate
()
}
// Otherwise let's execute the query first
else
{
// Updating the data in the resultset of the session view
resultSet
.
load
(
function
(
error
)
{
// Only update when data was retrieved successfully
if
(
error
)
{
// TODO: Error handling
console
.
log
(
`Error: could not load result:
${
error
}
`
)
}
else
{
// Update the current view based on the new info
this
.
scheduleUIUpdate
()
// TODO: shouldn't this be setCurrentView??
}
})
}
}
maybeLogRead
()
{
let
item
=
this
.
cascadingView
?.
resultSet
.
singletonItem
if
(
item
)
{
let
auditItem
=
Cache
.
createItem
(
AuditItem
.
constructor
,
{
action
:
"
read
"
})
//TODO
item
.
link
(
auditItem
,
"
changelog
"
)
}
}
maybeLogUpdate
()
{
if
(
this
.
cascadingView
?.
context
==
undefined
)
{
return
}
let
syncState
=
this
.
cascadingView
?.
resultSet
.
singletonItem
?.
syncState
if
(
syncState
.
changedInThisSession
)
{
let
fields
=
syncState
.
updatedFields
// TODO: serialize
let
item
=
this
.
cascadingView
?.
resultSet
.
singletonItem
if
(
item
)
{
let
auditItem
=
Cache
.
createItem
(
AuditItem
.
constructor
,
{
//TODO
contents
:
JSON
.
stringify
(
AnyCodable
(
fields
)),
//TODO
action
:
"
update
"
,
})
item
.
link
(
auditItem
,
"
changelog
"
)
realmWriteIfAvailable
(
this
.
realm
,
function
()
{
syncState
.
changedInThisSession
=
false
})
}
else
{
console
.
log
(
"
Could not log update, no Item found
"
)
}
}
}
getPropertyValue
(
name
)
{
let
type
=
new
Mirror
(
this
)
for
(
var
child
of
type
.
children
)
{
if
(
child
.
label
==
name
||
child
.
label
==
"
_
"
+
name
)
{
return
child
.
value
}
}
return
""
}
aliases
=
{}
getSubscript
(
propName
)
{
let
alias
=
this
.
aliases
[
propName
]
if
(
alias
)
{
let
value
=
this
.
settings
.
get
(
alias
.
key
)
switch
(
alias
.
type
)
{
case
"
bool
"
:
return
value
??
false
case
"
string
"
:
return
value
??
""
case
"
int
"
:
return
value
??
0
case
"
double
"
:
return
value
??
0
default
:
return
null
}
}
return
null
}
setSubscript
(
propName
,
newValue
)
{
let
alias
=
this
.
aliases
[
propName
]
if
(
alias
)
{
this
.
settings
.
set
(
alias
.
key
,
AnyCodable
(
newValue
))
//TODO
let
x
=
newValue
if
(
typeof
x
===
"
boolean
"
)
{
x
?
alias
.
on
()
:
alias
.
off
()
}
this
.
scheduleUIUpdate
(
true
)
}
else
{
console
.
log
(
`Cannot set property
${
propName
}
, does not exist on context`
)
}
}
get
showSessionSwitcher
()
{
return
this
[
"
showSessionSwitcher
"
]
==
true
}
set
showSessionSwitcher
(
value
)
{
this
[
"
showSessionSwitcher
"
]
=
value
}
get
showNavigationBinding
()
{
return
//TODO
// [weak self] in self?.showNavigation ?? false
}
set
showNavigationBinding
(
value
)
{
// [weak self] in self?.showNavigation = $0//TODO
}
get
showNavigation
()
{
return
this
[
"
showNavigation
"
]
==
true
}
set
showNavigation
(
value
)
{
this
[
"
showNavigation
"
]
=
value
}
constructor
({
name
,
podAPI
,
cache
,
realm
,
settings
,
installer
,
sessions
=
null
,
//TODO
views
,
cascadingView
=
null
,
//TODO
navigation
,
renderers
,
indexerAPI
})
{
super
()
this
.
name
=
name
this
.
podAPI
=
podAPI
this
.
cache
=
cache
this
.
realm
=
realm
this
.
settings
=
settings
this
.
installer
=
installer
this
.
sessions
=
sessions
this
.
views
=
views
this
.
cascadingView
=
cascadingView
this
.
navigation
=
navigation
this
.
renderers
=
renderers
this
.
indexerAPI
=
indexerAPI
// TODO: FIX
this
.
cascadingView
?.
context
=
this
this
.
indexerAPI
.
context
=
this
}
}
class
SubContext
extends
MemriContext
{
parent
:
MemriContext
constructor
(
name
:
string
,
context
:
MemriContext
,
session
:
Session
)
{
let
views
=
new
Views
(
context
.
realm
)
super
({
name
:
name
,
podAPI
:
context
.
podAPI
,
cache
:
context
.
cache
,
realm
:
context
.
realm
,
settings
:
context
.
settings
,
installer
:
context
.
installer
,
sessions
:
Cache
.
createItem
(
Sessions
.
constructor
),
views
:
views
,
// cascadingView: context.cascadingView,
navigation
:
context
.
navigation
,
renderers
:
context
.
renderers
,
indexerAPI
:
context
.
indexerAPI
})
this
.
parent
=
context
this
.
closeStack
=
context
.
closeStack
views
.
context
=
this
this
.
sessions
?.
setCurrentSession
(
session
)
}
}
/// Represents the entire application user interface. One can imagine in the future there being multiple applications, each aimed at a
/// different way to represent the data. For instance an application that is focussed on voice-first instead of gui-first.
class
RootContext
extends
MemriContext
{
cancellable
?:
AnyCancellable
subContexts
=
[]
// TODO: Refactor: Should installer be moved to rootmain?
constructor
(
name
:
string
,
key
:
string
)
{
super
()
//TODO
let
podAPI
=
new
PodAPI
(
key
)
let
cache
=
new
Cache
(
podAPI
)
let
realm
=
cache
.
realm
super
({
name
:
name
,
podAPI
:
podAPI
,
cache
:
cache
,
realm
:
realm
,
settings
:
new
Settings
(
realm
),
installer
:
new
Installer
(
realm
),
sessions
:
Cache
.
createItem
(
Sessions
.
constructor
,
{
uid
:
Cache
.
getDeviceID
()}),
views
:
new
Views
(
realm
),
navigation
:
new
MainNavigation
(
realm
),
renderers
:
new
Renderers
(),
indexerAPI
:
new
IndexerAPI
()
})
this
.
podAPI
=
podAPI
this
.
cache
=
cache
this
.
realm
=
realm
globalCache
=
cache
// TODO: remove this and fix edges
MapHelper
.
shared
.
realm
=
realm
// TODO: How to access realm in a better way?
this
.
cascadingView
?.
context
=
this
let
takeScreenShot
=
function
(){
// Make sure to record a screenshot prior to session switching
this
.
currentSession
?.
takeScreenShot
()
// Optimize by only doing this when a property in session/view/dataitem has changed
}
// TODO: Refactor: This is a mess. Create a nice API, possible using property wrappers
this
.
aliases
=
{
showSessionSwitcher
:
new
Alias
(
"
device/gui/showSessionSwitcher
"
,
"
bool
"
,
takeScreenShot
),
showNavigation
:
new
Alias
(
"
device/gui/showNavigation
"
,
"
bool
"
,
takeScreenShot
),
}
this
.
cache
.
scheduleUIUpdate
=
{
[
weak
self
]
in
self
?.
scheduleUIUpdate
(
$0
)
}
//TODO
this
.
navigation
.
scheduleUIUpdate
=
{
[
weak
self
]
in
self
?.
scheduleUIUpdate
(
$0
)
}
//TODO
// Make settings global so it can be reached everywhere
globalSettings
=
this
.
settings
//TODO
}
createSubContext
(
session
)
{
let
subContext
=
new
SubContext
(
"
Proxy
"
,
this
,
session
)
this
.
subContexts
.
push
(
subContext
)
return
subContext
}
boot
()
{
// Make sure memri is installed properly
this
.
installer
.
installIfNeeded
(
this
)
{
/*#if (targetEnvironment(simulator)
// Reload for easy adjusting
self.views.context = self
self.views.install()
#endif*/
// Load views configuration
this
.
views
.
load
(
this
,
function
()
{
// Update view when sessions changes
this
.
cancellable
=
this
.
sessions
?.
objectWillChange
.
sink
(
function
()
{
this
.
scheduleUIUpdate
()
})
this
.
currentSession
?.
access
()
this
.
currentSession
?.
currentView
?.
access
()
// Load current view
this
.
updateCascadingView
()
})
}
}
mockBoot
()
{
try
{
this
.
boot
()
return
this
}
catch
(
error
)
{
console
.
log
(
error
)
}
return
this
}
}
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Explore
Projects
Groups
Snippets