Compare commits
1 Commits
480e77bbb9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ee550301e9 |
147
boot_js.ml
147
boot_js.ml
@ -4,7 +4,8 @@ module NVG = Graphv_webgl
|
|||||||
|
|
||||||
let _ =
|
let _ =
|
||||||
Logs.set_reporter (Human.Logs_reporter.console_reporter ());
|
Logs.set_reporter (Human.Logs_reporter.console_reporter ());
|
||||||
Logs.set_level (Some Debug)
|
Logs.set_level (Some Debug);
|
||||||
|
Logs.debug (fun m -> m "hello")
|
||||||
|
|
||||||
module Log = (val Logs.src_log Logs.default : Logs.LOG)
|
module Log = (val Logs.src_log Logs.default : Logs.LOG)
|
||||||
|
|
||||||
@ -26,20 +27,23 @@ let scale_canvas (canvas : Dom_html.canvasElement Js.t) =
|
|||||||
canvas##.style##.width := width;
|
canvas##.style##.width := width;
|
||||||
canvas##.style##.height := height
|
canvas##.style##.height := height
|
||||||
|
|
||||||
let webgl_initialize canvas =
|
let _ =
|
||||||
|
let canvas =
|
||||||
|
Js.Unsafe.coerce (Dom_html.getElementById_exn "canvas")
|
||||||
|
in
|
||||||
scale_canvas canvas;
|
scale_canvas canvas;
|
||||||
(* Graphv requires a stencil buffer to work properly *)
|
let webgl_ctx =
|
||||||
let attrs = WebGL.defaultContextAttributes in
|
(* Graphv requires a stencil buffer to work properly *)
|
||||||
attrs##.stencil := Js._true;
|
let attrs = WebGL.defaultContextAttributes in
|
||||||
match
|
attrs##.stencil := Js._true;
|
||||||
WebGL.getContextWithAttributes canvas attrs |> Js.Opt.to_option
|
match
|
||||||
with
|
WebGL.getContextWithAttributes canvas attrs |> Js.Opt.to_option
|
||||||
| None ->
|
with
|
||||||
print_endline "Sorry your browser does not support WebGL";
|
| None ->
|
||||||
raise Exit
|
print_endline "Sorry your browser does not support WebGL";
|
||||||
| Some ctx -> ctx
|
raise Exit
|
||||||
|
| Some ctx -> ctx
|
||||||
let graphv_initialize webgl_ctx =
|
in
|
||||||
let open NVG in
|
let open NVG in
|
||||||
let vg =
|
let vg =
|
||||||
create
|
create
|
||||||
@ -49,74 +53,57 @@ let graphv_initialize webgl_ctx =
|
|||||||
(* File in this case is actually the CSS font name *)
|
(* File in this case is actually the CSS font name *)
|
||||||
Text.create vg ~name:"sans" ~file:"sans" |> ignore;
|
Text.create vg ~name:"sans" ~file:"sans" |> ignore;
|
||||||
webgl_ctx##clearColor 0.3 0.3 0.32 1.;
|
webgl_ctx##clearColor 0.3 0.3 0.32 1.;
|
||||||
vg
|
|
||||||
|
|
||||||
let request_animation_frame () =
|
(*
|
||||||
let t, s = Lwt.wait () in
|
let render ev =
|
||||||
let (_ : Dom_html.animation_frame_request_id) =
|
webgl_ctx##clear
|
||||||
Dom_html.window##requestAnimationFrame
|
(webgl_ctx##._COLOR_BUFFER_BIT_
|
||||||
(Js.wrap_callback (fun (time : float) -> Lwt.wakeup s time))
|
lor webgl_ctx##._DEPTH_BUFFER_BIT_
|
||||||
|
lor webgl_ctx##._STENCIL_BUFFER_BIT_);
|
||||||
|
let device_ratio = Dom_html.window##.devicePixelRatio in
|
||||||
|
begin_frame vg ~width:canvas##.width ~height:canvas##.height
|
||||||
|
~device_ratio;
|
||||||
|
Transform.scale vg ~x:device_ratio ~y:device_ratio;
|
||||||
|
ignore Human.Panel.Ui.(panel vg Gg.P2.o test ev);
|
||||||
|
(*
|
||||||
|
Path.begin_ vg ;
|
||||||
|
Path.rect vg ~x:40. ~y:40. ~w:320. ~h:320. ;
|
||||||
|
set_fill_color vg ~color:Color.(rgba ~r:154 ~g:203 ~b:255 ~a:200) ;
|
||||||
|
fill vg ;
|
||||||
|
Transform.translate vg ~x:200. ~y:200. ;
|
||||||
|
Transform.rotate vg ~angle:(time *. 0.0005) ;
|
||||||
|
Text.set_font_face vg ~name:"sans" ;
|
||||||
|
Text.set_size vg ~size:48. ;
|
||||||
|
Text.set_align vg ~align:Align.(center lor middle) ;
|
||||||
|
set_fill_color vg ~color:Color.white ;
|
||||||
|
Text.text vg ~x:0. ~y:0. "Hello World!" ; *)
|
||||||
|
NVG.end_frame vg
|
||||||
in
|
in
|
||||||
t
|
Dom_html.window##requestAnimationFrame
|
||||||
|
(Js.wrap_callback (fun _ -> render Human.Event.empty))
|
||||||
let render_stream canvas webgl_ctx vg
|
|> ignore;*)
|
||||||
(render : NVG.t -> ?time:float -> Gg.p2 -> Human.I.t -> unit) :
|
|
||||||
Human.I.t Lwt_stream.t -> unit Lwt.t =
|
|
||||||
Lwt_stream.iter_n (fun i ->
|
|
||||||
request_animation_frame () >>= fun time ->
|
|
||||||
webgl_ctx##clear
|
|
||||||
(webgl_ctx##._COLOR_BUFFER_BIT_
|
|
||||||
lor webgl_ctx##._DEPTH_BUFFER_BIT_
|
|
||||||
lor webgl_ctx##._STENCIL_BUFFER_BIT_);
|
|
||||||
let device_ratio = Dom_html.window##.devicePixelRatio in
|
|
||||||
NVG.begin_frame vg ~width:canvas##.width ~height:canvas##.height
|
|
||||||
~device_ratio;
|
|
||||||
NVG.Transform.scale vg ~x:device_ratio ~y:device_ratio;
|
|
||||||
render vg ~time Gg.P2.o i;
|
|
||||||
NVG.end_frame vg;
|
|
||||||
Lwt.return_unit)
|
|
||||||
|
|
||||||
open Human
|
|
||||||
|
|
||||||
let _ =
|
|
||||||
let canvas =
|
|
||||||
Js.Unsafe.coerce (Dom_html.getElementById_exn "canvas")
|
|
||||||
in
|
|
||||||
let webgl_ctx = webgl_initialize canvas in
|
|
||||||
let vg = graphv_initialize webgl_ctx in
|
|
||||||
let open Js_of_ocaml_lwt.Lwt_js_events in
|
let open Js_of_ocaml_lwt.Lwt_js_events in
|
||||||
let open Nottui in
|
|
||||||
let gravity_pad = Gravity.make ~h:`Negative ~v:`Negative in
|
|
||||||
let gravity_crop = Gravity.make ~h:`Positive ~v:`Negative in
|
|
||||||
let body = Lwd.var (Lwd.pure Ui.empty) in
|
|
||||||
let wm = Widgets.window_manager (Lwd.join (Lwd.get body)) in
|
|
||||||
Nav.test_populate () >>= fun test_store ->
|
|
||||||
let ui = Widgets.(h_node_area (test_store, [ [] ])) in
|
|
||||||
let root =
|
|
||||||
Lwd.set body
|
|
||||||
(Lwd.map ~f:(Ui.resize ~pad:gravity_pad ~crop:gravity_crop) ui);
|
|
||||||
Widgets.window_manager_view wm
|
|
||||||
in
|
|
||||||
|
|
||||||
let events, push_event = Lwt_stream.create () in
|
|
||||||
let images =
|
|
||||||
Human.Nottui_lwt.render vg
|
|
||||||
~size:(Gg.P2.v canvas##.width canvas##.height)
|
|
||||||
events root
|
|
||||||
in
|
|
||||||
async (fun () ->
|
async (fun () ->
|
||||||
render_stream canvas webgl_ctx vg
|
buffered_loop (make_event Dom_html.Event.keydown)
|
||||||
(fun vg ?(time = 0.) p i ->
|
Dom_html.document (fun ev _ ->
|
||||||
let _ = time in
|
webgl_ctx##clear
|
||||||
Log.debug (fun m ->
|
(webgl_ctx##._COLOR_BUFFER_BIT_
|
||||||
m "Drawing image: p=%a n=%a" Gg.V2.pp p
|
lor webgl_ctx##._DEPTH_BUFFER_BIT_
|
||||||
(I.Draw.pp ~attr:A.dark)
|
lor webgl_ctx##._STENCIL_BUFFER_BIT_);
|
||||||
i);
|
let device_ratio = Dom_html.window##.devicePixelRatio in
|
||||||
let p' = I.Draw.node vg A.dark p i in
|
begin_frame vg ~width:canvas##.width ~height:canvas##.height
|
||||||
|
~device_ratio;
|
||||||
|
Transform.scale vg ~x:device_ratio ~y:device_ratio;
|
||||||
|
Human.Panel.Ui.(
|
||||||
|
render_lwt vg Gg.P2.o
|
||||||
|
(Human.Event_js.evt_of_jskey `Press ev))
|
||||||
|
>>= fun p ->
|
||||||
Logs.debug (fun m ->
|
Logs.debug (fun m ->
|
||||||
m "Drawing finished: p'=%a" Gg.V2.pp p'))
|
m "Drawing finished at point: %a" Gg.V2.pp p);
|
||||||
images);
|
NVG.end_frame vg;
|
||||||
buffered_loop (make_event Dom_html.Event.keydown) Dom_html.document
|
Lwt.return_unit))
|
||||||
(fun ev _ ->
|
|
||||||
Lwt.return
|
(* Dom_html.document##.onkeydown
|
||||||
@@ push_event (Some (`Keys [ Event_js.evt_of_jskey ev ])))
|
:= Dom.handler (fun (evt : Dom_html.keyboardEvent Js.t) ->
|
||||||
|
render (Human.Event_js.evt_of_jskey `Press evt) ;
|
||||||
|
Js._false ) *)
|
||||||
|
|||||||
1
doc/console.drawio
Normal file
1
doc/console.drawio
Normal file
@ -0,0 +1 @@
|
|||||||
|
<mxfile host="Electron" modified="2023-03-13T23:21:20.375Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/20.8.16 Chrome/106.0.5249.199 Electron/21.3.5 Safari/537.36" etag="Z2Mfk2ZBbl7WkBZ1-Pcz" version="20.8.16" type="device"><diagram name="Page-1" id="LQ51MIGZ5u8MJkVY-xBH">7Vttc9o4EP41zPQ6k4xlY2M+BpJcP+RuOpOZa/PpRtgyqJUtzogA/fUn2TK2LBFIwC9tmkyItdabtc8+u1qLgTONt3+mcLn4i4aIDGwr3A6c24FtD32bfwrBLhfYo3EumKc4zEWgFDziH0gKLSld4xCtlIqMUsLwUhUGNElQwBQZTFO6UatFlKijLuEcaYLHABJd+gWHbJFLfdcq5Z8Qni+KkYEl78SwqCwFqwUM6aYicu4GzjSllOVX8XaKiFi7Yl3ydvcH7u4nlqKEndIA3aL7+bd/bD8ef/nu3D7cpA/0SvbyDMlaPjBBjAtSFIjVgoEoJPwvu2nhSClaQhsLJATgmn8gskKiGaGwaPfxuuznQ3IF/pCLwXbFCjO05fOfLFhMuADwyxVL6Xc0pYSmXJLQhNecRJiQmggSPE94MeArgLh88oxShrnubuSNGIehGGayWWCGHpcwEGNuOFC5LKXrJERicfjTTAicIfKZrjDD1NjnQ63Cvu8FTfEPmjBYTD/ihXsYYyIgP6XrFPOObOtvxJU/kevNO0Xbg4oEe3hws0I0Rizd8So71TQ2JR7dAnWLChYdKYPSBOb7nkqU8AsJlFeAxvY01AxsjwgtrpYwUdTr/bcWAM8W5WojZ3cjwEHTWCzZpKzDr+byPynaGDurXnHLz0FRFTrAGjljpHctkMj1xQeWY/AFyIcphrYuMHrozTzX00fnuLnB2WAwFhBMZqvlftDfoiZEZiXvxTlejbAzahZ6vjXzdc1e7UcvYCRJ8MAEahy42uCYwOQQVVW48UVyETelCwWgQpAERWJY/kQELld4lg1rnclGW5VlJDl5OjkNgYGcwLAxdhqb2KkGjV76oLr6znckL9D3QYV6nqpQTZ9GdTamTV2ZUqVV/anreJYJVVQsmBwiPwo0PPA7XuCjWdSkIuyho2jCcTRV2G26fV9bdhTyWFkWacoWdE4TSO5KaQ3gZZ0HSpdSHd8QYzsJe7hmVFUW2mL2VTS/dmXpqXLndit7zgq7opDwx/1aLTxlPYzcoly2y0pFw9cgI4oiOzAiQ3r/Ahlikd6CC77SfAYBeqmipDoG0zl6sUcz0FJEIMPP6vRMuJFNP9Msetkzha/g060BL5++bFPD3n4Sb4ej1wkcD0DL/tWg5baErLMYydWcg6WBojHXELni1+gash/ZXUWe/zTpMoBfcxl6NOYYXIbdmPfu1GcU10+K/zD7jFfF1fUgwUV+ODQhwbdnToaEkjeu3SpzgCO00bidn+pBnGZcyNBWAVvvIZ9XYz5E3yqIDFe+Z5ulhxMZqwwMIo0BwHI7MCUx8rzYRbrK02r1LUuKYTI/uLM4ldRqW8RjSO8wHHYsFSquHg57Bm7zmuI2YNiadEB2DRNX4wQEiqz+MQYadxlpAD1RLjLe7znWsG01/B/p9thqrAEcTUWM/ivTcO8hXwAclSAB0DUyalUjejTe0+ivlW3dydhoMmPwU5Ctvq/L3yC2ZcUzEIaRZdKUfK/UpBW7NVr1NSMeum0a8bBLI75E2u88G345gupDqubkLGDuD36xNKAh5dNTJ/MqnL0ZzP3DJ/BOxafbCD7B+EBc1FKSAejHJa7eSURqj45tEVp9gwW6SUc2E1z23zGB0amGfwBGLYWbI80+QXv22cet/Ug9AjDq+i2C+7O4+IbePPc/i1fkPvu9sSxmWbH0jy1m8fp0mARYoGNfPO6TL76sgb5l99pD/336xvJc/20O3F0VsvU3OU1vLLtJfLxXD1Jsyo6fOLE7dSF6aNjTWOQi6Ybje5QeIunks0vnIslMW3b9PYzVbr7B0aOc3wegX3PQoHaIqrET0LxYfv0r1375HTrn7n8=</diagram></mxfile>
|
||||||
BIN
doc/console.drawio.png
Normal file
BIN
doc/console.drawio.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
doc/console.odp
Normal file
BIN
doc/console.odp
Normal file
Binary file not shown.
BIN
doc/console.pptx
Normal file
BIN
doc/console.pptx
Normal file
Binary file not shown.
17
doc/deck_example.ml
Normal file
17
doc/deck_example.ml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
let rec copy_blocks buffer r w =
|
||||||
|
match%lwt Lwt_io.read_into r buffer 0 (Bytes.length buffer) with
|
||||||
|
| 0 -> Lwt.return_unit
|
||||||
|
| bytes_read ->
|
||||||
|
let%lwt () = Lwt_io.write_from_exactly w buffer 0 bytes_read in
|
||||||
|
copy_blocks buffer r w
|
||||||
|
|
||||||
|
let run () =
|
||||||
|
((let%lwt server =
|
||||||
|
Lwt_io.establish_server (Lwt_unix.ADDR_INET (Unix.inet_addr_any, 8765))
|
||||||
|
(fun (r, w) ->
|
||||||
|
let buffer = Bytes.create (16 * 1024) in
|
||||||
|
copy_blocks buffer r w)
|
||||||
|
in
|
||||||
|
Lwt.return server) : Lwt_io.server Lwt.t) |> ignore
|
||||||
|
|
||||||
|
let () = Lwt_main.run run ();
|
||||||
1
doc/factorial.drawio
Normal file
1
doc/factorial.drawio
Normal file
@ -0,0 +1 @@
|
|||||||
|
<mxfile host="Electron" modified="2023-03-14T02:21:12.395Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/20.8.16 Chrome/106.0.5249.199 Electron/21.3.5 Safari/537.36" etag="kySTQQLOUsgllAWFhiTX" version="20.8.16" type="device"><diagram name="Page-1" id="LQ51MIGZ5u8MJkVY-xBH">7Vtbb6M4FP41kdKRWmEIhDy2Tbv70F2NNNLO9GnlgEk8dXCWOE06v35tMOFik9AkXGY6rVrhg318Od+5+BgPrPvl7o8IrhZ/UR+RgWn4u4E1HZimMwH8vyC8JQRrMkkI8wj7CQlkhC/4B5JEQ1I32EfrQkVGKWF4VSR6NAyRxwo0GEV0W6wWUFLsdQXnSCF88SBRqV+xzxYJ1bWNjP4nwvNF2jMw5JslTCtLwnoBfbrNkayHgXUfUcqSp+XuHhGxdum6JO0eK97uBxahkNVpgKbocf79H9NdTr6+WNOn2+iJXksur5Bs5IQJYpwQIU+sFvQYjTAUbEL+F9dwCO/ubhbxp7l4Sik+fhWzZG9y6Zz/NlS+z54KLdYrGGqbzKD3Mo/oJvSvPUpoNLBuxURCzMRYKljC5Yo/hLP1Ki4bOMiPWojFYAskCOCG/0NkjWIw/RsQCpms++mmNO1heA2u0gHzxU3GXJwHJ8eTT6lmYVImQztBX7AlH/sUiImziL6g+2Rq05CGvOZdgAkpkSDB85AXCQoEh1cUMcyheSvJjIqpbheYoS8r6InutlwJOS1eOyQEz2d/R+AMkc90zZePinYeRwyKcgyfShWW2PfF6O8WfBl+0JDBdOQBLzzCJSZCne/pJsKckWn8jbb7mQumaFcJUrCHPjcZiC4Ri954lbSBVJZtpmv2SNIWOT1LaVCq93zPKdMA/iCV4B0KYTqKRhyFq1iU660cnUBqSKNlNVBJ2uaouhgS/YXXFjDG1gSprHOw3eMy6Sbt2rhA774zc2xH7Z3j5hbHnZUV8TepAZJS1kv8oN2SKNCKGTquMXM1+I0BLk1mRacl+7fe4iWBsUHT2aqcXTxoXcRLGR8AoBpHPgsCV2s8i7s1LmuOZPCiM08joDFPoDn7NNHZpxIYLu6AFI+RuqC9pzjuhcryO9+VHDDglRJ1nIJEHUWeWnE2Jk1VmFKkefkV1/EsHcqJWNhyiNzAU/DA3ziei2ZBk4IwR1ZBEpaliMLUiMJqShSusuzI5zsBWaQRW9A5DSF5yKglgGd1nqiIzGJxfEeMvUnYww2jRWGhHWbfRPMbW5aec2+mO8k5LrylhZBP91u+8BxzGNtpOWsXl9KG70FGEASmp0WG9P8pMsQinYILvtJ8BB46VFGaOgajOTrIUQ+0CBHI8GtxeDrcyKafaRy/7C2FW8CnXQJeMnzZpoS9/SBOh6PTCRwroGX+atCyW0LWWRbJVpyDoYCiMdcQ2OJX6xriH8kuR09+mnQZwC25DDUaszQuw2zMe3fqM9Ln54L/0PuMdwXW5SDBRq4/0iHBNWdWjITMbtzYecsBjpiNxvW8rgexmnEhI7MI2DKHZFyN+RB1qyByYln+riqVsY7BIPZ5AKx2A10aI8mkXYRVkogrb1kiDMN55c6irlEr7RGPIb3DcNgyilCx1XDY0dg2pynbBjRbkw6MXcOGq3EDBNIzi2MWaNJlpAHUYwCRI//IsYZpFsP/saqPrcYawFJElB1dfIR8ASgZSABUiYxblYgajfc0+mtlW1cbG01mDH4KY6vu63InN22p8gz4fmDoxCWPl5pU5bJtdVVNNtvU5FGXmnyJ3N95inw4jOpDvqZ2KjBxCr9YLlCT9+mpp3kXzk4Gc//wCZy6+LQbwSdwKoKjljINQP1q4vpjhKWT8bFtQqunWKCblGQzAWb//RIY19X7ChS1FHKOFfUE7alnL7f3xc8Axl2fJNg/i4dv6PS5/5m8NP/Z781lOsqcpn9qMZPXpw9KgAE69sWTPvniyyroKZvXHvrv+vvKc/23Pm63i5Atn+Y0va/sJu/xUT1Iuic7/tWJ2akLUUPDnsYiF8k2HN+j9BBJtb9fOhdJerNlls9ijHbTDZYa5fz+CPo9Hxu4xe/aO/4K2lLPNqtuoQ2TC13JdTB2tS+kd7ji1pobauULFcP4dtgwdz0s5lWnaZkkL5UNwc3JLORdtOEwfxvtZG4nk+LLb+12OVTu2l01O4B67K8aMB8f7xJfKarN3+kzdPblhJ0YL2Y3aBP3kl1Dth7+Bw==</diagram></mxfile>
|
||||||
BIN
doc/factorial.drawio.png
Normal file
BIN
doc/factorial.drawio.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
18
notes.org
18
notes.org
@ -24,21 +24,3 @@
|
|||||||
* principles?
|
* principles?
|
||||||
an "anywhere" programming environment
|
an "anywhere" programming environment
|
||||||
|
|
||||||
* 221211
|
|
||||||
ok you got the scroll box mostly working so next:
|
|
||||||
** fix the scroll jump bugs
|
|
||||||
** setup better keybindings
|
|
||||||
** fix cursor and active focus indicators
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* 221210 -
|
|
||||||
** need to resolve the issue with the ui.t Resize type.
|
|
||||||
this is an issue with the direction of the determination of the .height and .width fields of Ui.t
|
|
||||||
|
|
||||||
currently you were planning to combine update_sensors and update_size
|
|
||||||
|
|
||||||
in the original nottui.ml library, are Ui.t.w and Ui.t.h determined from the top down orbottom up?
|
|
||||||
the bottom up, becahse they are chars
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
37
unicom.org
37
unicom.org
@ -1,37 +0,0 @@
|
|||||||
UNICOM
|
|
||||||
|
|
||||||
unifying tools for thought and commmunication
|
|
||||||
|
|
||||||
At the top level is a kind of "trace" that connects all the tools together
|
|
||||||
and records the exact use of all the tools in a session. Traces are the only
|
|
||||||
kind of document in the system, and are basically written by a user when
|
|
||||||
they interact with the system. Users can store, retrieve, view, and modify
|
|
||||||
traces as a way of controlling system state, retrieving history, and sharing
|
|
||||||
information?
|
|
||||||
|
|
||||||
|
|
||||||
tools for thought:
|
|
||||||
- todo lists
|
|
||||||
- personal journal
|
|
||||||
- calendar
|
|
||||||
- calculator/spreadsheet
|
|
||||||
- health, fitness, finance data tracking, analysis
|
|
||||||
- Integrated Development Environment [for development/configuration of all these tools]
|
|
||||||
|
|
||||||
|
|
||||||
tools for communication:
|
|
||||||
- collaborative documents
|
|
||||||
video / .txt file
|
|
||||||
- content discovery,display,labeling?
|
|
||||||
twitter / netflix / books
|
|
||||||
- direct messages
|
|
||||||
sms / email
|
|
||||||
- group messages
|
|
||||||
private (chat) / private (irc,twitter)
|
|
||||||
- audio/music production
|
|
||||||
sound driver / DAW
|
|
||||||
- cad
|
|
||||||
KiCad / OpenSCAD
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user