Skip to content

Commit

Permalink
Fixes the iOS issue that prevented play after a period of time.
Browse files Browse the repository at this point in the history
  • Loading branch information
JudahGabriel committed Feb 22, 2024
1 parent 44de718 commit 62c7c9f
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 8 deletions.
1 change: 1 addition & 0 deletions Chavah.NetCore/wwwroot/js/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
.when("/admin/songs", { redirectTo: "/admin" })
.when("/admin/donations", createRoute("/AdminDonations.html", RouteAccess.Admin))
.when("/admin/users", createRoute("/AdminUsers.html", RouteAccess.Admin))
.when("/admin/ioslogs", createRoute("/AdminIOSLogs.html", RouteAccess.Admin))

.otherwise({
redirectTo: "/nowplaying",
Expand Down
23 changes: 23 additions & 0 deletions Chavah.NetCore/wwwroot/js/Controllers/AdminIOSLogsController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace BitShuva.Chavah {
export class AdminIOSLogsController {

static $inject = [
"iosAudioPlayer"
];

iOSLogs = "";

constructor(private readonly iosAudioPlayer: IOSAudioPlayer) {
}

$onInit() {
this.refreshLogs();
}

refreshLogs() {
this.iOSLogs = this.iosAudioPlayer.logs.join("\r\r");
}
}

App.controller("AdminIOSLogsController", AdminIOSLogsController);
}
2 changes: 1 addition & 1 deletion Chavah.NetCore/wwwroot/js/Services/AudioPlayerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ namespace BitShuva.Chavah {

// On the Chavah iOS app, we won't actually use HTML5 audio. See IOSAudioPlayer for details why.
if (this.iosAudioPlayer.isIOSWebApp) {
this.audio = new IOSAudioPlayer();
this.audio = this.iosAudioPlayer;
}

this.audio.addEventListener("ended", () => this.ended());
Expand Down
64 changes: 60 additions & 4 deletions Chavah.NetCore/wwwroot/js/Services/IOSAudioPlayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ namespace BitShuva.Chavah {
private _duration = 0;
private readonly eventTarget = new EventTarget();
private readonly _isIOSWebApp: boolean;
public readonly logs: string[] = [];

public error: MediaError | null = null;

constructor() {
this._isIOSWebApp = navigator.userAgent.includes("Chavah iOS WKWebView");

if (this.isIOSWebApp) {
// Our iOS webview will send these events to use when their equivalents happen in native.
window.addEventListener("iosaudioended", () => this.dispatchEvent(new CustomEvent("ended")));
Expand All @@ -42,7 +42,7 @@ namespace BitShuva.Chavah {

set src(val: string | null) {
this._src = val;
this.postMessageToNative("src", val);
this.postMessageToNative("src", IOSAudioPlayer.getEncodedUrlForIOS(val));
}

get currentTime(): number {
Expand Down Expand Up @@ -92,7 +92,7 @@ namespace BitShuva.Chavah {
album,
artist,
songTitle,
artwork
artwork: IOSAudioPlayer.getEncodedUrlForIOS(artwork)
});
this.postMessageToNative("mediasession", args);
}
Expand All @@ -101,18 +101,74 @@ namespace BitShuva.Chavah {
return this._isIOSWebApp;
}

private dispatchEvent<K extends keyof HTMLMediaElementEventMap>(ev: HTMLMediaElementEventMap[K]): void {
addLog(message: string): void {
// Is this message a time update? If so, special handling there:
// We get many time updates (potentially hundreds per song), so we don't want to flood the log with those.
// Instead, if the last message also was a timeupdate, replace that one with this new one.
const lastMessage = this.logs.length > 0 ? this.logs[this.logs.length - 1] : null;
if (message.includes("timeupdate") && lastMessage && lastMessage.includes("timeupdate")) {
this.logs[this.logs.length - 1] = message;
} else {
// OK, just treat it normally then.
this.logs.push(message);
}

// Don't let the log grow past 100 items.
if (this.logs.length > 100) {
this.logs.splice(0, 1); // remove the first (oldest) log
}
}

private dispatchEvent(ev: CustomEvent | ErrorEvent): void {
// Add it to the logs.
// Caveat: if it's a timeupdate message, and the last message was also timeupdate, replace the last one with this one.
// Otherwise, we'll get flooded with too many timeupdate messages.
let message = `Received ${ev.type || "[unknown]"} event from native code.`;
if (ev.type === "timeupdate") {
message = `${message} - ${this.currentTime}:${this.duration}`;
}
this.addLog(message);
this.eventTarget.dispatchEvent(ev);
}

private postMessageToNative(message: string, args?: string | number | null) {
const iosWebViewAudioHandler = this.isIOSWebApp && (window as any).webkit?.messageHandlers?.audiohandler;
if (iosWebViewAudioHandler) {
this.addLog(`Sending ${message} to native code. Args: ${args || "[null]"}`);

iosWebViewAudioHandler.postMessage({
action: message,
details: args
});
} else {
this.addLog(`Went to send ${message} to native, but there is no iOS WebViewAudioHandler ${this.isIOSWebApp} - ${(window as any).webkit?.messageHandlers?.audiohandler}`);
}
}

/**
* Returns the URL encoded. This is needed because iOS 16 and earlier don't properly handle URIs with spaces in them.
* Also, if the URL is relative, we need to make it absolute. (Example relative path: station ID announcements)
* @param url
*/
private static getEncodedUrlForIOS(url: string | null): string | null {
if (!url) {
return null;
}

if (url.startsWith("/")) {
url = "https://messianicradio.com" + url;
}

if (url.includes(" ")) {
try {
return encodeURI(url)
} catch (encodeError) {
console.warn("Error encoding URL due to an error", url, encodeError);
return url;
}
}

return url;
}
}

Expand Down
20 changes: 20 additions & 0 deletions Chavah.NetCore/wwwroot/views/AdminIOSLogs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<section class="admin-page" ng-controller="AdminIOSLogsController as vm">
<div class="row">
<div class="col-xs-12 col-sm-offset-1 col-sm-10">
<div class="row">
<div class="col-xs-12 col-sm-3">
<div ng-include="Partials.adminSidebar" ng-init="adminPage = 'ioslogs'"></div>
</div>

<div class="col-sm-9">
<button ng-click="vm.refreshLogs()">Refresh</button>
<br />
<br />

<textarea ng-model="vm.iOSLogs" style="height: 500px; width: 100%;"></textarea>
</div>
</div>
</div>
</div>
</section>

9 changes: 6 additions & 3 deletions Chavah.NetCore/wwwroot/views/partials/AdminSidebar.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
<li role="presentation" ng-class="{ active: adminPage === 'users' }">
<a href="#/admin/users">Users</a>
</li>
<li ng-class="{ active: adminPage === 'songedits' }">
<li role="presentation" ng-class="{ active: adminPage === 'songedits' }">
<a href="#/admin/songedits">Pending song edits</a>
</li>
<li ng-class="{ active: adminPage === 'tags' }">
<li role="presentation" ng-class="{ active: adminPage === 'tags' }">
<a href="#/admin/tags">Tags</a>
</li>
<li ng-class="{ active: adminPage === 'logs' }">
<li role="presentation" ng-class="{ active: adminPage === 'logs' }">
<a href="#/admin/logs">Logs</a>
</li>
<li role="presentation" ng-class="{ active: adminPage === 'ioslogs' }">
<a href="#/admin/ioslogs">iOS Logs</a>
</li>
</ul>
</div>
Binary file modified MessiahsMusicFundDisbursement.xlsx
Binary file not shown.

0 comments on commit 62c7c9f

Please sign in to comment.