Skip to content

Commit 0751c26

Browse files
committed
Implement Reverso Context #19
Speak ReversoContext by press special button or Cmd or Alt
1 parent 6508fdd commit 0751c26

File tree

5 files changed

+130
-29
lines changed

5 files changed

+130
-29
lines changed

ReaderTranslator/Components/GTranslator.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ struct GTranslator : ViewRepresentable, WKScriptsSetup {
1919
private let defaultUrl = "https://translate.google.com?sl=auto&tl=ru"
2020

2121
class Coordinator: WKCoordinator {
22+
@Published var selectedText = ""
23+
2224
override init(_ parent: WKScriptsSetup) {
2325
super.init(parent)
2426

@@ -84,6 +86,27 @@ struct GTranslator : ViewRepresentable, WKScriptsSetup {
8486
}
8587
}
8688

89+
extension GTranslator.Coordinator: WKScriptMessageHandler {
90+
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
91+
switch message.name {
92+
case "onSelectionChange":
93+
if let value = message.body as? String {
94+
selectedText = value
95+
}
96+
case "onContextMenu":
97+
print("onContextMenu")
98+
case "onBodyLoaded":
99+
print("onBodyLoaded")
100+
case "onKeyDown":
101+
if let code = message.body as? String {
102+
if code == "MetaLeft" { SpeechSynthesizer.speak(stopSpeaking: true, isVoiceEnabled: true) }
103+
}
104+
default:
105+
print("webkit.messageHandlers.\(message.name).postMessage() isn't found")
106+
}
107+
}
108+
}
109+
87110

88111

89112

ReaderTranslator/Components/ReversoContext.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ struct ReversoContext : ViewRepresentable, WKScriptsSetup {
1717
static var hasSentTranslateAction = false
1818

1919
class Coordinator: WKCoordinator {
20+
@Published var selectedText = ""
21+
2022
override init(_ parent: WKScriptsSetup) {
2123
super.init(parent)
2224

2325
$selectedText
24-
.debounce(for: 0.5, scheduler: RunLoop.main)
26+
.debounce(for: 0.1, scheduler: RunLoop.main)
2527
.removeDuplicates()
2628
.sink { text in
2729
print("ReversoContext_$selectedText", text)
@@ -64,6 +66,29 @@ struct ReversoContext : ViewRepresentable, WKScriptsSetup {
6466
}
6567
}
6668

69+
extension ReversoContext.Coordinator: WKScriptMessageHandler {
70+
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
71+
switch message.name {
72+
case "onSelectionChange":
73+
if let value = message.body as? String {
74+
self.selectedText = value
75+
SpeechSynthesizer.speak(text: self.selectedText, stopSpeaking: true, isVoiceEnabled: true)
76+
}
77+
case "onContextMenu":
78+
print("onContextMenu")
79+
case "onBodyLoaded":
80+
print("onBodyLoaded")
81+
case "onKeyDown":
82+
if let code = message.body as? Int {
83+
if code == 18 || code == 91 {
84+
SpeechSynthesizer.speak(text: self.selectedText, stopSpeaking: true, isVoiceEnabled: true)
85+
}
86+
}
87+
default:
88+
print("webkit.messageHandlers.\(message.name).postMessage() isn't found")
89+
}
90+
}
91+
}
6792

6893

6994

ReaderTranslator/Components/WebKit/WKCoordinator.swift

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,11 @@ class WKCoordinator: NSObject {
1616
@ObservedObject var store = Store.shared
1717
var cancellableSet: Set<AnyCancellable> = []
1818

19-
@Published var selectedText = ""
20-
2119
init(_ parent: WKScriptsSetup) {
2220
self.parent = parent
2321
}
2422
}
2523

26-
extension WKCoordinator: WKScriptMessageHandler {
27-
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
28-
switch message.name {
29-
case "onSelectionChange":
30-
if let value = message.body as? String {
31-
selectedText = value
32-
}
33-
case "onContextMenu":
34-
print("onContextMenu")
35-
case "onBodyLoaded":
36-
print("onBodyLoaded")
37-
case "onKeyDown":
38-
if let code = message.body as? String {
39-
if code == "MetaLeft" { SpeechSynthesizer.speak(stopSpeaking: true, isVoiceEnabled: true) }
40-
}
41-
default:
42-
print("webkit.messageHandlers.\(message.name).postMessage() isn't found")
43-
}
44-
}
45-
}
46-
4724
extension WKCoordinator: WKNavigationDelegate {
4825
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
4926
if let url = navigationAction.request.url {

ReaderTranslator/Components/WebKit/WKRepresenter.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ struct WKRepresenter: ViewRepresentable, WKScriptsSetup {
2020
static private var views = [Int: WKPageView]()
2121

2222
class Coordinator: WKCoordinator {
23+
@Published var selectedText = ""
24+
2325
override init(_ parent: WKScriptsSetup) {
2426
print("WKRepresenter_Coordinator_init")
2527
super.init(parent)
@@ -76,6 +78,28 @@ struct WKRepresenter: ViewRepresentable, WKScriptsSetup {
7678
}
7779
}
7880

81+
extension WKRepresenter.Coordinator: WKScriptMessageHandler {
82+
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
83+
switch message.name {
84+
case "onSelectionChange":
85+
// if let value = message.body as? String {
86+
// selectedText = value
87+
// }
88+
print("onSelectionChange is not implemented")
89+
case "onContextMenu":
90+
print("onContextMenu")
91+
case "onBodyLoaded":
92+
print("onBodyLoaded")
93+
case "onKeyDown":
94+
// if let code = message.body as? String {
95+
// if code == "MetaLeft" { SpeechSynthesizer.speak(stopSpeaking: true, isVoiceEnabled: true) }
96+
// }
97+
print("onKeyDown is not implemented")
98+
default:
99+
print("webkit.messageHandlers.\(message.name).postMessage() isn't found")
100+
}
101+
}
102+
}
79103

80104

81105

ReaderTranslator/Components/WebKit/WKScriptsSetup.swift

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,74 @@ import Combine
1010
import WebKit
1111

1212
private let script = """
13+
(function() {
14+
function debounce(func, wait, immediate) {
15+
// 'private' variable for instance
16+
// The returned function will be able to reference this due to closure.
17+
// Each call to the returned function will share this common timer.
18+
var timeout;
19+
20+
// Calling debounce returns a new anonymous function
21+
return function() {
22+
// reference the context and args for the setTimeout function
23+
var context = this,
24+
args = arguments;
25+
26+
// Should the function be called now? If immediate is true
27+
// and not already in a timeout then the answer is: Yes
28+
var callNow = immediate && !timeout;
29+
30+
// This is the basic debounce behaviour where you can call this
31+
// function several times, but it will only execute once
32+
// [before or after imposing a delay].
33+
// Each time the returned function is called, the timer starts over.
34+
clearTimeout(timeout);
35+
36+
// Set the new timeout
37+
timeout = setTimeout(function() {
38+
39+
// Inside the timeout function, clear the timeout variable
40+
// which will let the next execution run when in 'immediate' mode
41+
timeout = null;
42+
43+
// Check if the function already ran with the immediate flag
44+
if (!immediate) {
45+
// Call the original function with apply
46+
// apply lets you define the 'this' object as well as the arguments
47+
// (both captured before setTimeout)
48+
func.apply(context, args);
49+
}
50+
}, wait);
51+
52+
// Immediate mode and no wait timer? Execute the function..
53+
if (callNow) func.apply(context, args);
54+
}
55+
}
56+
57+
function _send(method, value) {
58+
webkit.messageHandlers[method].postMessage(value)
59+
}
60+
61+
var sendIn100 = debounce(_send, 100)
62+
var sendIn1000 = debounce(_send, 1000)
63+
1364
document.onselectionchange = function() {
1465
var txt = document.getSelection().toString()
1566
16-
webkit.messageHandlers.onSelectionChange.postMessage(txt)
67+
sendIn1000('onSelectionChange', txt)
1768
}
1869
window.oncontextmenu = function() {
1970
var txt = document.getSelection().toString()
2071
21-
webkit.messageHandlers.onContextMenu.postMessage("txt")
72+
sendIn100('onContextMenu', txt)
2273
}
2374
document.body.onload = function() {
24-
webkit.messageHandlers.onBodyLoaded.postMessage("txt")
75+
sendIn100('onBodyLoaded', '')
2576
}
2677
document.body.onkeydown = function(event) {
27-
webkit.messageHandlers.onKeyDown.postMessage(event.code)
78+
sendIn100('onKeyDown', event.keyCode)
2879
}
80+
})()
2981
"""
3082

3183
protocol WKScriptsSetup {
@@ -34,7 +86,7 @@ protocol WKScriptsSetup {
3486
}
3587

3688
extension WKScriptsSetup {
37-
func setupScripts(view: WKPageView, coordinator: WKCoordinator) {
89+
func setupScripts<T: WKCoordinator>(view: WKPageView, coordinator: T) where T: WKScriptMessageHandler {
3890
let userContentController = view.configuration.userContentController
3991

4092
userContentController.add(coordinator, name: "onSelectionChange")

0 commit comments

Comments
 (0)