I am fighting a problem with SignalR in MVC application. I have created an MVC 4 project based on internet template and instaled SignalR nuget packet. Hub class looks like this:
public class yourAppHub : Hub
{
static Timer t;
public yourAppHub()
{
t = new Timer(5000);
t.Elapsed += Hello;
t.Start();
}
public void Hello(object sender, ElapsedEventArgs e)
{
Clients.All.refreshPage("hello");
}
}
And this is what I have on master page(_Layout.cshtml):
#if (Request.IsAuthenticated)
{
<script>
$(function () {
var con = $.hubConnection();
var hub = con.createHubProxy('yourAppHub');
hub.on('refreshPage', function (message) { alert(message); });
con.start(function () { hub.invoke('Hello'); });
});
</script>
}
When I am logging in I get 2 alerts every 5 seconds. Why 2 alerts? But if I go to home page, then go to contact form, then on some another page, alerts begin to continuously pop up. I can't get what I have done wrong here. I expect alert to pop up once in 5 seconds.
I have followed this wonderful tutorial http://www.dotnetcurry.com/aspnet-mvc/826/server-side-timer-aspnet-mvc-signalr . It implements singleton and starts timer not in hub but in that singleton. Now it works as expected.
Related
I am creating an AppDrawer (all in one place to launch shortcuts).
My JSCallBack bridge class has a method that should delete a shortcut, then refresh the page. But, it is unable to successfully refresh the page.
AppDrawerMain.java
public class AppDrawerMain extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(AppDrawerMain.class.getResource("main.fxml"));
Parent root = (Parent) fxmlLoader.load();
Scene scene = new Scene(root, 1280, 720);
String css = this.getClass().getResource("application.css").toExternalForm();
scene.getStylesheets().add(css);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
AppDrawerController.java
public class AppDrawerController implements Initializable {
#FXML
private Button refreshButton;
#FXML
private WebView webView;
WebEngine webEngine;
JSCallBack jsCallBack;
//this function essentially generates the html code for the webview
public String loadApps(){
return "<div class=\"container\"><img src=\"file:/"+imagePath+"\"/><p class=\"title\">"+displayName+"</p><div class=\"overlay\"></div><div class=\"button-open\" onclick=\"app.processOnClickOpen(\'"+id+"\')\"> Open </div><div class=\"button-option\" onclick=\"app.processOnClickOption(\'"+id+"\')\"> Edit </div></div>"
}
//refresh the page
private void refreshPage(String html){
webEngine.loadContent(html);
webEngine.reload();
}
#SneakyThrows
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
refreshButton.setOnAction(event -> {
refreshPage(loadApps());
});
webEngine = webView.getEngine();
webEngine.getLoadWorker().stateProperty().addListener((obs, oldValue, newValue)-> {
if (newValue == Worker.State.SUCCEEDED) {
JSObject jsObject = (JSObject) webEngine.executeScript("window");
jsCallBack = new JSCallBack(webEngine); //declared this way to avoid GC
jsObject.setMember("app", jsCallBack);
}
});
webEngine.setJavaScriptEnabled(true);
var html = loadApps();
webEngine.loadContent(html);
}
//The bridge class
public class JSCallBack {
protected JSCallBack() {}
//no refresh needed here
public void processOnClickOpen(String id) {
log("Before open");
onAppOpen(id);
log("After open");
}
//The part that isnt working
public void processOnClickOption(String id) {
//deleting the apps works fine
webEngine.loadContent(loadApps()+loadApps()); //trying to refresh the page isnt fine
webEngine.reload();
}
}
}
The problem I am having is here:
public void processOnClickOption(String id) {
//refreshPage(loadApps()+loadApps()) (not working either)
//refreshButton.fire() (not working either)
webEngine.loadContent(loadApps()+loadApps()); //trying to refresh the page isnt fine
webEngine.reload(); //not working
}
I tried adding location.reload() to the JavaScript function in the script itself, but it did not work.
I tried adding the refreshButton.fire() to the processOnClickOption(), which should reload the page if clicked on manually, but it did work either.
I tried to set a new WebEngine in the bridge class itself. Also, it did not work.
I added a log to see if there was an issue with threading, but WebEngine mentions that it was on the JavaFX Application thread, so that is not the problem either.
This is the main WebView:
If I click on the edit button, it should remove it from the WebView and refresh the page like this expected outcome:
Unfortunately, after clicking the edit button it will delete the files on the backend side, but the WebView is not refreshed, so the app stays there. I would like to get help on this.
I want to get real-time notifications with sound when an order is placed into my app. I'm using laravel 8 and pusher to that. I get real-time alerts but the sound does not work perfectly. When an order is placed first time sound does not play, after that sound is played perfectly. Here is my code structure ...
My event class
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class MyEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $message;
public function __construct($message)
{
$this->message = $message;
}
public function broadcastOn()
{
return ['my-channel'];
}
public function broadcastAs()
{
return 'my-event';
}
}
Here is the js code
Pusher.logToConsole = true;
var pusher = new Pusher('***pusher key***', {
cluster: 'ap2'
});
var channel = pusher.subscribe('my-channel');
channel.bind('my-event', function(data) {
alert(data.message)
playAudio();
});
function playAudio() {
var x = new Audio('http://127.0.0.1:8000/backend/assets/notification.mp3');
// Show loading animation.
var playPromise = x.play();
if (playPromise !== undefined) {
playPromise.then(_ => {
x.play();.
})
.catch(error => {
});
}
}
Here is my controller
public function store(){
event(new MyEvent('1 new order has been placed'));
return 'Success';
}
When I hit this route for the first time, real-time alert message is shown but the sound does not play. It shows this error
DOMException: play() failed because the user didn't interact with the document first.
https://some link
And it works perfectly after first time
I would like to give a sound notification to the admin if the order is placed, now how can I do that?
With the latest updates of browsers, it is now impossible to start a sound with the code without the user clicking a button.
I would like to make my LibGDX game work as a Facebook instant game. So far I have managed to set up the game client as shown in the quick start guide like this:
<!DOCTYPE html>
<html>
<head>
<title>My LibGDX Game</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta
id="gameViewport"
name="viewport"
content="width=device-width initial-scale=1"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="full-screen" content="yes" />
<meta name="screen-orientation" content="portrait" />
</head>
<body>
<div align="center" id="embed-html"></div>
<script src="https://connect.facebook.net/en_US/fbinstant.6.3.js"></script>
<script src="main.js"></script>
<script type="text/javascript" src="html/html.nocache.js"></script>
</body>
<script>
function handleMouseDown(evt) {
evt.preventDefault();
evt.stopPropagation();
window.focus();
}
function handleMouseUp(evt) {
evt.preventDefault();
evt.stopPropagation();
}
document
.getElementById("embed-html")
.addEventListener("mousedown", handleMouseDown, false);
document
.getElementById("embed-html")
.addEventListener("mouseup", handleMouseUp, false);
</script>
</html>
In my main.js file which is separate from the GWT generated JS code, I am able to use the Facebook Instant games SDK to get the username and ID like this:
FBInstant.initializeAsync().then(function () {
var progress = 0;
var interval = setInterval(function () {
progress += 3;
FBInstant.setLoadingProgress(progress);
if (progress >= 99) {
clearInterval(interval);
FBInstant.startGameAsync().then(function () {
console.log("shit stsrted");
var playerId = FBInstant.player.getID(); // Get Facebook ID
var playerName = FBInstant.player.getName(); // Get Facebook Name
console.log(playerId);
console.log(playerName);
});
}
}, 100);
console.log("loaded");
});
Now I need to show an Ad and leaderboard on the GameOver screen, but how can I add Leaderboards and In-App Ads If the code for that is in JavaScript? I tried to dig into the GWT compiled JS code to somehow find the GameOver function but that code is unreadable. Is there a way to do this in the Java code maybe inject JavaScript and let GWT compile it with Java?
GWT project supports calling native JS from Java, previously through JSNI which allowed you to write JavaScript as a comment in method in Java world, but now there is a better solution - JSInterop - http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJsInterop.html
I came across your question when learning about it myself, and this blog explained it well for my use case:
https://lofidewanto.medium.com/learn-once-use-everywhere-javascript-in-java-for-web-browser-ec1b9657232c
Here's what I did:
Libgdx is multiplatform and I want to preserve the ability to port my game to mobile platforms, so I create a Java interface that handles loading and displaying of Ads (interstitials and rewarded videos):
public interface AdRequestHandler {
void loadInterstitial();
void showInterstitial();
void loadRewardedVideo();
void showRewardedVideo();
}
My GWT/HTML will only target Facebook instant games, so I provide an implementation of above interface:
public class FacebookAdRequestHandler implements AdRequestHandler {
private FacebookAdRequestService facebookAdRequestService = new FacebookAdRequestService();
#Override
public void loadInterstitial() {
facebookAdRequestService .loadInterstitialImpl();
}
#Override
public void showInterstitial() {
facebookAdRequestHandlerNative.showInterstitialImpl();
}
#Override
public void loadRewardedVideo() {
facebookAdRequestService.loadRewardedVideoImpl();
}
#Override
public void showRewardedVideo() {
facebookAdRequestService.showRewardedVideoImpl();
}
}
The implementation simply calls a Service class that does all the native JS calls:
#JsType(namespace = JsPackage.GLOBAL, name = "FacebookAdRequestHandler", isNative = true)
public class FacebookAdRequestService {
public native void loadInterstitialImpl();
public native void showInterstitialImpl();
public native void loadRewardedVideoImpl();
public native void showRewardedVideoImpl();
}
now for the JS part, I take your main.js file in the project under html/webapp
FBInstant.initializeAsync().then(function () {
var progress = 0;
var interval = setInterval(function () {
progress += 3;
FBInstant.setLoadingProgress(progress);
if (progress >= 99) {
clearInterval(interval);
FBInstant.startGameAsync().then(function () {
//var playerId = FBInstant.player.getID(); // Get Facebook ID
//var playerName = FBInstant.player.getName(); // Get Facebook Name
});
}
}, 100);
});
FacebookAdRequestHandler = function () {
var preloadedInterstitial = null;
var preloadedRewardedVideo = null;
}
FacebookAdRequestHandler.prototype.loadInterstitialImpl = function () {
FBInstant.getInterstitialAdAsync(
'123123123123_123123123123' // Your Ad Placement Id
).then(function(interstitial) {
// Load the Ad asynchronously
preloadedInterstitial = interstitial;
return preloadedInterstitial.loadAsync();
}).then(function() {
console.log('Interstitial preloaded');
}).catch(function(err){
console.error('Interstitial failed to preload: ' + err.message);
});
}
FacebookAdRequestHandler.prototype.showInterstitialImpl = function () {
preloadedInterstitial.showAsync()
.then(function() {
// Perform post-ad success operation
console.log('Interstitial ad finished successfully');
})
.catch(function(e) {
console.error(e.message);
});
}
FacebookAdRequestHandler.prototype.loadRewardedVideoImpl = function () {
FBInstant.getRewardedVideoAsync(
'456456456456_456456456456' // Your Ad Placement Id
).then(function(rewarded) {
// Load the Ad asynchronously
preloadedRewardedVideo = rewarded;
return preloadedRewardedVideo.loadAsync();
}).then(function() {
console.log('Rewarded video preloaded');
}).catch(function(err){
console.error('Rewarded video failed to preload: ' + err.message);
});
}
FacebookAdRequestHandler.prototype.showRewardedVideoImpl = function () {
preloadedRewardedVideo.showAsync()
.then(function() {
// Perform post-ad success operation
console.log('Rewarded video watched successfully');
})
.catch(function(e) {
console.error(e.message);
});
}
I've taken the code for loading and showing the interstitial directly from FB's documentation for their Instant Games - In-App Ads for Instant Games
And I've actually taken your example to resolve an issue of initializing and going around the progress bar reporting.
I've tested it for interstitials so far and that displays the ad correctly. What needs work is making sure to only display the ad when it loaded (maybe just do a null check inside showInterstitialImpl.
Now this is only a one sided solution, it doesn't report back to the Java part that anything was displayed, you can do that as well using #JsFunction annotation to create some callbacks
I have got the following code which will create a notification for a user when the browser is open:
Server-side:
[HubName("notificationhub")]
public class ChatHub : Hub
{
private readonly static ConnectionMapping<string> _connections =
new ConnectionMapping<string>();
public void SendNewNotification()
{
string notification_text = "This is a test notification";
Clients.All.triggerNotification(notification_text);
}
}
Client side:
var notification_hub;
$(function () {
notification_hub = $.connection.notificationhub;
$.connection.hub.start();
notification_hub.client.triggerNotification = function (message) {
var notification = new Notification('Test notification', {
body: message,
icon: "~/content/img/favicon/favicon.ico",
vibrate: [200, 100, 200],
});
}
This works as expected when the browser is currently on screen. However, I also want this same piece of client-side code to execute if the user has minimised the browser (or running it in the background if it is a mobile device). The intended effect would be to have the 'push' notification generated for the user. At the moment, however, this is not the case.
Any suggestions on how to achieve this?
I have a simple app, which displays a list of available signalR hubs. A user selects a hub and it connects to it, this subscribes an event to add messages to a table on the page. The user can then send messaged to that hub which will also fire the subscription adding that message to the table. This all works great.
Now if the user selects another hub, the app connects and sets up a new subscription, however the original subscription still fires causing duplicate messages to be added to the table. Each time the hub is changed further subscriptions get added causing one send to result in many messages in the table.
I have tried disconnecting the hub, disposing the hub and trying to remove the subscription with hubProxy.off(eventName), but nothing seems to work, other than a page reload.
Here is the code I have just added the onHub changed function as this is where everything is happening.
Any ideas appreciated. :)
function HubViewModel() {
var self = this;
self.hubConnection = '';
self.hub = '';
$.getScript("../signalR/hubs");
self.hubs = ko.observableArray();
self.selectedHub = ko.observable();
self.messageText = ko.observable();
self.messageCollection = ko.observableArray();
self.hubChanged = function () {
// Setup hub connection.
$.connection.hub.url = "../signalR";
self.hubConnection = $.hubConnection();
// Get the selected hub name.
var selectedHubName;
_.each(self.hubs(), function(item) {
if (item.hubId == self.selectedHub()) {
selectedHubName = item.hubName;
}
});
// Check for a selected connection
if (self.selectedHub()) {
// Create proxy.
self.hub = self.hubConnection.createHubProxy(selectedHubName);
// Remove any existing listener(s).
self.hub.off('addNewMessageToPage');
// Setup listener.
self.hub.On('addNewMessageToPage', function (sender, message) {
self.messageCollection().push({ hubName: selectedHubName, name: selectedHubName, message: message, dateTime: new Date().toLocaleString() });
$('#hubMessageGrid').dxDataGrid('instance').refresh();
});
// start connection.
self.hubConnection.start()
.done(function() {
toastr.success('hub connected');
$('#sendMessageButton').click(function() {
self.hub.invoke('sendAll', 'hub management page', self.messageText());
self.messageText('');
});
})
.fail(function(error) {
toastr.error('hub connection ' + error);
});
}
};
You can to disconnect the hub first by calling the self.hub.stop(); function
You need to pass the exact same handler instance when unsubscribing. Passing a different instance (even if the function body is the same) will not remove the handler.
See https://learn.microsoft.com/en-us/javascript/api/#microsoft/signalr/hubconnection?view=signalr-js-latest#off-string---args--any-------void-