Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
emiMobile
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Mac Stephens
emiMobile
Commits
7fa82e6d
Commit
7fa82e6d
authored
Oct 22, 2026
by
Mac Stephens
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix WC device management, SMS verification handling, and map marker loading
parent
13ffaf77
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
230 additions
and
169 deletions
+230
-169
.gitignore
.gitignore
+12
-0
Api.Service.pas
emiMobileServer/Source/Api.Service.pas
+1
-2
Api.ServiceImpl.pas
emiMobileServer/Source/Api.ServiceImpl.pas
+22
-61
Auth.ServiceImpl.pas
emiMobileServer/Source/Auth.ServiceImpl.pas
+24
-32
Ws.DataModel.pas
emiMobileServer/Source/Ws.DataModel.pas
+9
-5
emiMobileServer.ini
emiMobileServer/bin/emiMobileServer.ini
+1
-1
View.DeviceManager.dfm
webEMIMobile/View.DeviceManager.dfm
+9
-0
View.DeviceManager.html
webEMIMobile/View.DeviceManager.html
+16
-11
View.DeviceManager.pas
webEMIMobile/View.DeviceManager.pas
+121
-47
View.Map.pas
webEMIMobile/View.Map.pas
+15
-10
No files found.
.gitignore
View file @
7fa82e6d
...
@@ -28,3 +28,15 @@ emiMobileServer/Source/__recovery/
...
@@ -28,3 +28,15 @@ emiMobileServer/Source/__recovery/
*.dll
*.dll
webEMIMobile/config/__history/
webEMIMobile/config/__history/
emiMobileServer/Clients/BFD/Web/__history/
emiMobileServer/Clients/WC/Web/__history/
emiMobileServer/Shared/Server/__history/
emiMobileServer/Clients/WC/Web/css/__history/
emiMobileServer/Clients/BFD/Web/Win32/Debug/
emiMobileServer/Clients/WC/Web/Win32/Debug/
emiMobileServer/Source/Api.Service.pas
View file @
7fa82e6d
...
@@ -39,8 +39,7 @@ type
...
@@ -39,8 +39,7 @@ type
function
UnrevokeDevice
(
const
CredentialId
:
string
):
TJSONObject
;
function
UnrevokeDevice
(
const
CredentialId
:
string
):
TJSONObject
;
function
AddPendingDevice
(
const
DeviceName
,
PhoneNumber
,
function
AddPendingDevice
(
const
DeviceName
,
PhoneNumber
,
DeviceType
:
string
):
TJSONObject
;
DeviceType
:
string
):
TJSONObject
;
function
DeletePendingDevice
(
const
PhoneNumber
:
string
):
TJSONObject
;
function
DeleteDevice
(
DeviceId
:
Integer
):
TJSONObject
;
function
DeleteDevice
(
CredentialId
:
string
):
TJSONObject
;
function
SendAppLink
(
const
PhoneNumber
:
string
):
TJSONObject
;
function
SendAppLink
(
const
PhoneNumber
:
string
):
TJSONObject
;
function
UpdateDeviceUsername
(
const
CredentialId
,
Username
:
string
):
TJSONObject
;
function
UpdateDeviceUsername
(
const
CredentialId
,
Username
:
string
):
TJSONObject
;
end
;
end
;
...
...
emiMobileServer/Source/Api.ServiceImpl.pas
View file @
7fa82e6d
...
@@ -45,8 +45,7 @@ type
...
@@ -45,8 +45,7 @@ type
function
UnrevokeDevice
(
const
CredentialId
:
string
):
TJSONObject
;
function
UnrevokeDevice
(
const
CredentialId
:
string
):
TJSONObject
;
function
AddPendingDevice
(
const
DeviceName
,
PhoneNumber
,
function
AddPendingDevice
(
const
DeviceName
,
PhoneNumber
,
DeviceType
:
string
):
TJSONObject
;
DeviceType
:
string
):
TJSONObject
;
function
DeletePendingDevice
(
const
PhoneNumber
:
string
):
TJSONObject
;
function
DeleteDevice
(
DeviceId
:
Integer
):
TJSONObject
;
function
DeleteDevice
(
CredentialId
:
string
):
TJSONObject
;
function
SendAppLink
(
const
PhoneNumber
:
string
):
TJSONObject
;
function
SendAppLink
(
const
PhoneNumber
:
string
):
TJSONObject
;
function
UpdateDeviceUsername
(
const
CredentialId
,
Username
:
string
):
TJSONObject
;
function
UpdateDeviceUsername
(
const
CredentialId
,
Username
:
string
):
TJSONObject
;
end
;
end
;
...
@@ -1626,6 +1625,21 @@ begin
...
@@ -1626,6 +1625,21 @@ begin
try
try
q
.
Connection
:=
conn
;
q
.
Connection
:=
conn
;
// Note: Device names are unique regardless of capitalization or surrounding spaces.
q
.
SQL
.
Text
:=
'SELECT id FROM lems.device_registrations '
+
'WHERE UPPER(TRIM(device_name)) = UPPER(:NAME)'
;
q
.
ParamByName
(
'NAME'
).
AsString
:=
Trim
(
DeviceName
);
q
.
Open
;
if
not
q
.
IsEmpty
then
begin
q
.
Close
;
Result
.
AddPair
(
'status'
,
'error'
);
Result
.
AddPair
(
'message'
,
'A device with that name already exists. Enter a different device name.'
);
Exit
;
end
;
q
.
Close
;
// Reject if a pending entry with this phone already exists
// Reject if a pending entry with this phone already exists
q
.
SQL
.
Text
:=
q
.
SQL
.
Text
:=
'SELECT id FROM lems.device_registrations '
+
'SELECT id FROM lems.device_registrations '
+
...
@@ -1661,72 +1675,20 @@ begin
...
@@ -1661,72 +1675,20 @@ begin
end
;
end
;
end
;
end
;
function
TApiService
.
Delete
PendingDevice
(
const
PhoneNumber
:
string
):
TJSONObject
;
function
TApiService
.
Delete
Device
(
DeviceId
:
Integer
):
TJSONObject
;
var
var
conn
:
TUniConnection
;
conn
:
TUniConnection
;
q
:
TUniQuery
;
q
:
TUniQuery
;
normalizedPhone
:
string
;
begin
begin
RequireAdmin
;
RequireAdmin
;
Result
:=
TJSONObject
.
Create
;
Result
:=
TJSONObject
.
Create
;
TXDataOperationContext
.
Current
.
Handler
.
ManagedObjects
.
Add
(
Result
);
TXDataOperationContext
.
Current
.
Handler
.
ManagedObjects
.
Add
(
Result
);
try
if
DeviceId
<=
0
then
normalizedPhone
:=
NormalizePhoneE164Api
(
PhoneNumber
);
except
on
E
:
Exception
do
begin
Result
.
AddPair
(
'status'
,
'error'
);
Result
.
AddPair
(
'message'
,
E
.
Message
);
Exit
;
end
;
end
;
conn
:=
OpenLemsConnection
;
try
q
:=
TUniQuery
.
Create
(
nil
);
try
q
.
Connection
:=
conn
;
q
.
SQL
.
Text
:=
'DELETE FROM lems.device_registrations '
+
'WHERE phone_number = :PHONE AND status = ''pending'''
;
q
.
ParamByName
(
'PHONE'
).
AsString
:=
normalizedPhone
;
q
.
ExecSQL
;
if
q
.
RowsAffected
>
0
then
begin
Logger
.
Log
(
2
,
'TApiService.DeletePendingDevice - deleted phone: '
+
normalizedPhone
);
Result
.
AddPair
(
'status'
,
'ok'
);
Result
.
AddPair
(
'message'
,
'Pending device removed.'
);
end
else
begin
Result
.
AddPair
(
'status'
,
'error'
);
Result
.
AddPair
(
'message'
,
'Pending device not found.'
);
end
;
finally
q
.
Free
;
end
;
finally
conn
.
Free
;
end
;
end
;
function
TApiService
.
DeleteDevice
(
CredentialId
:
string
):
TJSONObject
;
var
conn
:
TUniConnection
;
q
:
TUniQuery
;
begin
RequireAdmin
;
Result
:=
TJSONObject
.
Create
;
TXDataOperationContext
.
Current
.
Handler
.
ManagedObjects
.
Add
(
Result
);
if
Trim
(
CredentialId
)
=
''
then
begin
begin
Result
.
AddPair
(
'status'
,
'error'
);
Result
.
AddPair
(
'status'
,
'error'
);
Result
.
AddPair
(
'message'
,
'
Credential
Id is required.'
);
Result
.
AddPair
(
'message'
,
'
Device
Id is required.'
);
Exit
;
Exit
;
end
;
end
;
...
@@ -1737,14 +1699,13 @@ begin
...
@@ -1737,14 +1699,13 @@ begin
q
.
Connection
:=
conn
;
q
.
Connection
:=
conn
;
q
.
SQL
.
Text
:=
q
.
SQL
.
Text
:=
'DELETE FROM lems.device_registrations '
+
'DELETE FROM lems.device_registrations '
+
'WHERE
credential_id = :C
ID'
;
'WHERE
id = :
ID'
;
q
.
ParamByName
(
'
CID'
).
AsString
:=
Trim
(
CredentialId
)
;
q
.
ParamByName
(
'
ID'
).
AsInteger
:=
DeviceId
;
q
.
ExecSQL
;
q
.
ExecSQL
;
if
q
.
RowsAffected
>
0
then
if
q
.
RowsAffected
>
0
then
begin
begin
Logger
.
Log
(
2
,
'TApiService.DeleteDevice - deleted credential: '
+
Logger
.
Log
(
2
,
'TApiService.DeleteDevice - deleted id: '
+
IntToStr
(
DeviceId
));
Copy
(
CredentialId
,
1
,
20
));
Result
.
AddPair
(
'status'
,
'ok'
);
Result
.
AddPair
(
'status'
,
'ok'
);
Result
.
AddPair
(
'message'
,
'Device deleted.'
);
Result
.
AddPair
(
'message'
,
'Device deleted.'
);
end
end
...
...
emiMobileServer/Source/Auth.ServiceImpl.pas
View file @
7fa82e6d
...
@@ -29,7 +29,7 @@ type
...
@@ -29,7 +29,7 @@ type
function
LoadUserByName
(
const
User
,
Agency
:
string
):
Boolean
;
function
LoadUserByName
(
const
User
,
Agency
:
string
):
Boolean
;
function
Decrypt
(
inStr
,
keyStr
:
AnsiString
):
AnsiString
;
function
Decrypt
(
inStr
,
keyStr
:
AnsiString
):
AnsiString
;
function
GenerateVerificationCode
:
string
;
function
GenerateVerificationCode
:
string
;
procedure
SendVerificationText
(
const
PhoneNumber
,
Code
:
string
)
;
function
SendVerificationText
(
const
PhoneNumber
,
Code
:
string
):
Boolean
;
// Returns True and sets AChallengeB64 if token is valid and of the expected type
// Returns True and sets AChallengeB64 if token is valid and of the expected type
function
VerifyChallengeToken
(
const
ChallengeToken
,
ExpectedType
:
string
;
function
VerifyChallengeToken
(
const
ChallengeToken
,
ExpectedType
:
string
;
out
AChallengeB64
:
string
):
Boolean
;
out
AChallengeB64
:
string
):
Boolean
;
...
@@ -209,7 +209,7 @@ begin
...
@@ -209,7 +209,7 @@ begin
Result
:=
FormatFloat
(
'000000'
,
value
mod
1000000
);
Result
:=
FormatFloat
(
'000000'
,
value
mod
1000000
);
end
;
end
;
procedure
TAuthService
.
SendVerificationText
(
const
PhoneNumber
,
Code
:
string
)
;
function
TAuthService
.
SendVerificationText
(
const
PhoneNumber
,
Code
:
string
):
Boolean
;
var
var
httpClient
:
THTTPClient
;
httpClient
:
THTTPClient
;
bodyStream
:
TStringStream
;
bodyStream
:
TStringStream
;
...
@@ -239,9 +239,10 @@ begin
...
@@ -239,9 +239,10 @@ begin
response
:=
httpClient
.
Post
(
response
:=
httpClient
.
Post
(
'https://api.twilio.com/2010-04-01/Accounts/'
+
'https://api.twilio.com/2010-04-01/Accounts/'
+
ServerConfig
.
twilioAccountSid
+
'/Messages.json'
,
bodyStream
);
ServerConfig
.
twilioAccountSid
+
'/Messages.json'
,
bodyStream
);
if
response
.
StatusCode
>=
300
then
Result
:=
(
response
.
StatusCode
>=
200
)
and
(
response
.
StatusCode
<
300
);
raise
Exception
.
CreateFmt
(
'Twilio error %d: %s'
,
if
not
Result
then
[
response
.
StatusCode
,
response
.
ContentAsString
]);
Logger
.
Log
(
2
,
Format
(
'Twilio rejected verification SMS, HTTP %d: %s'
,
[
response
.
StatusCode
,
response
.
ContentAsString
]));
finally
finally
bodyStream
.
Free
;
bodyStream
.
Free
;
end
;
end
;
...
@@ -308,6 +309,24 @@ begin
...
@@ -308,6 +309,24 @@ begin
end
;
end
;
code
:=
GenerateVerificationCode
;
code
:=
GenerateVerificationCode
;
try
if
not
SendVerificationText
(
normalizedPhone
,
code
)
then
begin
Result
.
AddPair
(
'status'
,
'error'
);
Result
.
AddPair
(
'message'
,
'The SMS provider rejected the verification message. Contact your administrator to check the phone number and SMS configuration.'
);
Exit
;
end
;
except
on
E
:
Exception
do
begin
Logger
.
Log
(
2
,
'--Error sending registration verification code: '
+
E
.
Message
);
Result
.
AddPair
(
'status'
,
'error'
);
Result
.
AddPair
(
'message'
,
'The verification code could not be sent. Please try again.'
);
Exit
;
end
;
end
;
// Note: Persist the code only after the SMS provider accepts the message.
q
:=
TUniQuery
.
Create
(
nil
);
q
:=
TUniQuery
.
Create
(
nil
);
try
try
...
@@ -330,33 +349,6 @@ begin
...
@@ -330,33 +349,6 @@ begin
q
.
Free
;
q
.
Free
;
end
;
end
;
try
SendVerificationText
(
normalizedPhone
,
code
);
except
on
E
:
Exception
do
begin
q
:=
TUniQuery
.
Create
(
nil
);
try
q
.
Connection
:=
authDB
.
ucLemsOCSO
;
q
.
SQL
.
Text
:=
'UPDATE lems.device_registrations '
+
'SET verification_code = NULL, verification_expires_at = NULL '
+
'WHERE phone_number = :PHONE AND status = ''pending'' '
+
'AND verification_code = :CODE'
;
q
.
ParamByName
(
'PHONE'
).
AsString
:=
normalizedPhone
;
q
.
ParamByName
(
'CODE'
).
AsString
:=
code
;
q
.
ExecSQL
;
finally
q
.
Free
;
end
;
Logger
.
Log
(
2
,
'--Error sending registration verification code: '
+
E
.
Message
);
Result
.
AddPair
(
'status'
,
'error'
);
Result
.
AddPair
(
'message'
,
'The verification code could not be sent. Please try again.'
);
Exit
;
end
;
end
;
Logger
.
Log
(
3
,
Format
(
Logger
.
Log
(
3
,
Format
(
'Verification code sent for device id %d and device name: %s'
,
'Verification code sent for device id %d and device name: %s'
,
[
deviceId
,
deviceName
]));
[
deviceId
,
deviceName
]));
...
...
emiMobileServer/Source/Ws.DataModel.pas
View file @
7fa82e6d
...
@@ -79,12 +79,16 @@ end;
...
@@ -79,12 +79,16 @@ end;
destructor
TWsDataModel
.
Destroy
;
destructor
TWsDataModel
.
Destroy
;
begin
begin
FListenerTimer
.
Enabled
:=
False
;
if
Assigned
(
FListenerTimer
)
then
FListenerTimer
.
Enabled
:=
False
;
FListenerTimer
.
Free
;
FListenerTimer
.
Free
;
FThread
.
Terminate
;
if
Assigned
(
FThread
)
then
FStopEvent
.
SetEvent
;
begin
FThread
.
WaitFor
;
FThread
.
Terminate
;
FThread
.
Free
;
FStopEvent
.
SetEvent
;
FThread
.
WaitFor
;
FThread
.
Free
;
end
;
FListenerDb
.
Free
;
FListenerDb
.
Free
;
FStopEvent
.
Free
;
FStopEvent
.
Free
;
inherited
;
inherited
;
...
...
emiMobileServer/bin/emiMobileServer.ini
View file @
7fa82e6d
[Settings]
[Settings]
LogFileNum
=
15
4
LogFileNum
=
15
5
webClientVersion
=
0.9.4.9
webClientVersion
=
0.9.4.9
MemoLogLevel
=
3
MemoLogLevel
=
3
FileLogLevel
=
4
FileLogLevel
=
4
...
...
webEMIMobile/View.DeviceManager.dfm
View file @
7fa82e6d
...
@@ -81,6 +81,15 @@ object FViewDeviceManager: TFViewDeviceManager
...
@@ -81,6 +81,15 @@ object FViewDeviceManager: TFViewDeviceManager
WidthPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnLogoutClick
OnClick = btnLogoutClick
end
end
object btnRefresh: TWebButton
Left = 620
Top = 50
Width = 75
Height = 25
Caption = 'Refresh'
ElementID = 'view.devmgr.btnrefresh'
OnClick = btnRefreshClick
end
object XDataWebClient: TXDataWebClient
object XDataWebClient: TXDataWebClient
Connection = DMConnection.ApiConnection
Connection = DMConnection.ApiConnection
Left = 800
Left = 800
...
...
webEMIMobile/View.DeviceManager.html
View file @
7fa82e6d
<div
class=
"container-fluid p-3 h-100 d-flex flex-column"
>
<div
class=
"container-fluid p-3 h-100 d-flex flex-column"
>
<div
class=
"d-flex align-items-center mb-3"
>
<div
class=
"d-flex align-items-center
flex-wrap gap-2
mb-3"
>
<h5
class=
"mb-0 me-auto"
>
Device Management
</h5>
<h5
class=
"mb-0 me-auto"
>
Device Management
</h5>
<button
id=
"view.devmgr.btnlogout"
class=
"btn btn-outline-secondary btn-sm me-2"
type=
"button"
>
<button
id=
"view.devmgr.btnlogout"
class=
"btn btn-outline-secondary btn-sm me-2"
type=
"button"
>
Log Out
Log Out
</button>
</button>
<button
id=
"view.devmgr.btnrefresh"
<button
id=
"view.devmgr.btnrefresh"
class=
"btn btn-outline-secondary btn-sm"
class=
"btn btn-outline-secondary btn-sm"
onclick=
"document.dispatchEvent(new CustomEvent('devmgr-refresh'))
"
>
type=
"button
"
>
Refresh
Refresh
</button>
</button>
</div>
</div>
...
@@ -26,9 +26,9 @@
...
@@ -26,9 +26,9 @@
<!-- Add pending device -->
<!-- Add pending device -->
<div
class=
"card mb-3"
>
<div
class=
"card mb-3"
>
<div
class=
"card-body py-2"
>
<div
class=
"card-body py-2"
>
<div
class=
"
d-flex align-items-start gap-2 flex-wrap
"
>
<div
class=
"
row g-2 align-items-start
"
>
<
span
class=
"fw-semibold text-nowrap small mt-2"
>
Add Device:
</span
>
<
div
class=
"col-12 col-md-auto"
><span
class=
"fw-semibold text-nowrap small"
>
Add Device:
</span></div
>
<div
style=
"width:180px;
"
>
<div
class=
"col-6 col-md-auto
"
>
<input
type=
"text"
<input
type=
"text"
id=
"view.devmgr.newname"
id=
"view.devmgr.newname"
class=
"form-control form-control-sm"
class=
"form-control form-control-sm"
...
@@ -36,7 +36,7 @@
...
@@ -36,7 +36,7 @@
required
>
required
>
<div
class=
"invalid-feedback small"
>
Device name is required.
</div>
<div
class=
"invalid-feedback small"
>
Device name is required.
</div>
</div>
</div>
<div
style=
"width:160px;
"
>
<div
class=
"col-6 col-md-auto
"
>
<input
type=
"tel"
<input
type=
"tel"
id=
"view.devmgr.newphone"
id=
"view.devmgr.newphone"
class=
"form-control form-control-sm"
class=
"form-control form-control-sm"
...
@@ -44,7 +44,7 @@
...
@@ -44,7 +44,7 @@
required
>
required
>
<div
class=
"invalid-feedback small"
>
Phone number is required.
</div>
<div
class=
"invalid-feedback small"
>
Phone number is required.
</div>
</div>
</div>
<div
style=
"width:140px;
"
>
<div
class=
"col-6 col-md-auto
"
>
<select
id=
"view.devmgr.newtype"
<select
id=
"view.devmgr.newtype"
class=
"form-select form-select-sm"
class=
"form-select form-select-sm"
required
>
required
>
...
@@ -56,20 +56,24 @@
...
@@ -56,20 +56,24 @@
</select>
</select>
<div
class=
"invalid-feedback small"
>
Device type is required.
</div>
<div
class=
"invalid-feedback small"
>
Device type is required.
</div>
</div>
</div>
<button
id=
"view.devmgr.btnadd"
<div
class=
"col-6 col-md-auto d-grid"
>
class=
"btn btn-primary btn-sm"
>
Add
</button>
<button
id=
"view.devmgr.btnadd"
class=
"btn btn-primary btn-sm"
>
Add
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Device table -->
<!-- Device table -->
<div
class=
"table-responsive flex-grow-1"
>
<div
class=
"flex-grow-1 overflow-auto"
>
<div
id=
"view.devmgr.cards"
class=
"d-md-none"
></div>
<div
class=
"table-responsive d-none d-md-block"
>
<table
class=
"table table-sm table-hover align-middle"
id=
"view.devmgr.table"
>
<table
class=
"table table-sm table-hover align-middle"
id=
"view.devmgr.table"
>
<thead
class=
"table-light sticky-top"
>
<thead
class=
"table-light sticky-top"
>
<tr>
<tr>
<th
style=
"min-width:130px;"
>
Device Name
</th>
<th
style=
"min-width:130px;"
>
Device Name
</th>
<th
style=
"min-width:100px;"
>
Device Type
</th>
<th
style=
"min-width:120px;"
>
Phone
</th>
<th
style=
"min-width:120px;"
>
Phone
</th>
<th
style=
"min-width:1
45
px;"
>
Verification Code
</th>
<th
style=
"min-width:1
10
px;"
>
Verification Code
</th>
<th
style=
"min-width:140px;"
>
Username
</th>
<th
style=
"min-width:140px;"
>
Username
</th>
<th>
Browser / User Agent
</th>
<th>
Browser / User Agent
</th>
<th
style=
"min-width:135px;"
>
Registered
</th>
<th
style=
"min-width:135px;"
>
Registered
</th>
...
@@ -82,6 +86,7 @@
...
@@ -82,6 +86,7 @@
<tbody
id=
"view.devmgr.tbody"
>
<tbody
id=
"view.devmgr.tbody"
>
</tbody>
</tbody>
</table>
</table>
</div>
<p
id=
"view.devmgr.empty"
class=
"text-muted d-none text-center py-4"
>
<p
id=
"view.devmgr.empty"
class=
"text-muted d-none text-center py-4"
>
No devices found.
No devices found.
</p>
</p>
...
...
webEMIMobile/View.DeviceManager.pas
View file @
7fa82e6d
This diff is collapsed.
Click to expand it.
webEMIMobile/View.Map.pas
View file @
7fa82e6d
...
@@ -98,17 +98,22 @@ end;
...
@@ -98,17 +98,22 @@ end;
procedure
TFViewMap
.
UpdateDeviceLocation
(
lat
,
lng
:
Double
);
procedure
TFViewMap
.
UpdateDeviceLocation
(
lat
,
lng
:
Double
);
begin
begin
if
userLocationMarker
=
nil
then
lfMap
.
BeginUpdate
;
begin
try
userLocationMarker
:=
lfMap
.
Markers
.
Add
;
if
userLocationMarker
=
nil
then
userLocationMarker
.
Title
:=
'<div class="small fw-semibold">You are here</div>'
;
begin
userLocationMarker
.
DataString
:=
'device'
;
userLocationMarker
:=
lfMap
.
Markers
.
Add
;
userLocationMarker
.
IconURL
:=
'assets/markers/location_dot.png'
;
userLocationMarker
.
Title
:=
'<div class="small fw-semibold">You are here</div>'
;
userLocationMarker
.
Visible
:=
False
;
userLocationMarker
.
DataString
:=
'device'
;
end
;
userLocationMarker
.
IconURL
:=
'assets/markers/location_dot.png'
;
userLocationMarker
.
Visible
:=
False
;
end
;
userLocationMarker
.
Latitude
:=
lat
;
userLocationMarker
.
Latitude
:=
lat
;
userLocationMarker
.
Longitude
:=
lng
;
userLocationMarker
.
Longitude
:=
lng
;
finally
lfMap
.
EndUpdate
;
end
;
end
;
end
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a 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