Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Controls on iOS #938

Open
c-mella opened this issue Aug 29, 2022 · 14 comments
Open

Custom Controls on iOS #938

c-mella opened this issue Aug 29, 2022 · 14 comments

Comments

@c-mella
Copy link

c-mella commented Aug 29, 2022

I am using usePlyr to create a custom plyr instance but on mobile the audio controls don't work. The icon shows as unmuted and doesn't unmute the audio when tapped. Has anyone else had this issue?

import React, { useState, useContext } from "react";

import type { APITypes } from "plyr-react";

import { usePlyr, PlyrProps, PlyrInstance } from "plyr-react";

import styles from "./Play.module.scss";

import { CoreContext } from "~context";
import hexToRgb from "~shared/utils/hexToRgb";

import { useGTM } from "~hooks/useGTM";

const CustomPlyr = React.forwardRef<APITypes, PlyrProps>((props, ref: any) => {
    const { playerStyle, size }: any = useContext(CoreContext);

    const { track } = useGTM();

    const { source, options = null } = props;

    const raptorRef = usePlyr(ref, { options, source });

    const { poster } = source || {};
    const { active } = options?.loop || {};
    const { autoplay } = options || {};

    const { gradient, subtitles, ui, full } = playerStyle;

    const [current, setCurrent] = useState(0);
    const [progress, setProgress] = useState(0);
    const [playing, setPlaying] = useState(autoplay);
    const [muted, setMuted] = useState(true);
    const [showLoopScreen, setShowLoop] = useState(false);
    const [counter, setCounter] = useState<number>(0);

    React.useEffect(() => {
        if (ref?.current?.plyr?.source === null) return;
        const api = ref?.current as { plyr: PlyrInstance };

        setMuted(api?.plyr?.volume === 0);

        const canPlay = () => {
            if (autoplay && !playing) {
                api?.plyr?.play();
            }
        };

        const onPlay = () => {
            setShowLoop(false);
            setPlaying(true);
        };

        const onPause = () => {
            setPlaying(false);
        };

        const onEnded = () => {
            setShowLoop(!active);
            setCounter(0);
            track({
                category: "adops",
                adOpsAction: "video",
                adOpsLabel: `video-completed`,
                event: "coreDataPush",
            });
        };

        const onUpdate = () => {
            setCurrent(Math.round(api?.plyr?.currentTime));

            if (api?.plyr?.paused === false) {
                if (current > 0.5) {
                    setProgress(
                        Number(
                            (api?.plyr?.currentTime / api?.plyr?.duration) * 100
                        )
                    );

                    if (progress > 1 && counter === 0) {
                        setCounter(1);
                        track({
                            category: "adops",
                            adOpsAction: "video",
                            adOpsLabel: `video-start`,
                            event: "coreDataPush",
                        });
                    }
                    if (progress > 25 && counter === 1) {
                        setCounter(2);
                        track({
                            category: "adops",
                            adOpsAction: "video",
                            adOpsLabel: `'video-played-25-percent`,
                            event: "coreDataPush",
                        });
                    }
                    if (progress > 50 && counter === 2) {
                        setCounter(3);
                        track({
                            category: "adops",
                            adOpsAction: "video",
                            adOpsLabel: `'video-played-50-percent`,
                            event: "coreDataPush",
                        });
                    }
                    if (progress > 75 && counter === 3) {
                        setCounter(4);
                        track({
                            category: "adops",
                            adOpsAction: "video",
                            adOpsLabel: `'video-played-75-percent`,
                            event: "coreDataPush",
                        });
                    }
                    if (progress < 5 && counter === 4) {
                        track({
                            category: "adops",
                            adOpsAction: "video",
                            adOpsLabel: `video-completed`,
                            event: "coreDataPush",
                        });
                        setShowLoop(!active);
                        track({
                            category: "adops",
                            adOpsAction: "video",
                            adOpsLabel: `video-start`,
                            event: "coreDataPush",
                        });
                        setCounter(1);
                    }
                }
            }
        };

        api?.plyr?.on("canplay", canPlay);
        api?.plyr?.on("playing", onPlay);
        api?.plyr?.on("pause", onPause);
        api?.plyr?.on("timeupdate", onUpdate);
        api?.plyr?.on("ended", onEnded);

        const offFuncs = () => {
            api?.plyr?.off("canplay", canPlay);
            api?.plyr?.off("playing", onPlay);
            api?.plyr?.off("pause", onPause);
            api?.plyr?.off("timeupdate", onUpdate);
            api?.plyr?.off("ended", onEnded);
        };

        if (ref?.current?.plyr?.source) {
            return offFuncs;
        }
    });

    if (!source || !ref) {
        return null;
    }

    return (
        <div
            data-size={size}
            data-full={full}
            className={styles.playercontainer}
        >
            {showLoopScreen || (!autoplay && current < 1 && !playing) ? (
                <div
                    className={styles.loopscreen}
                    style={{
                        backgroundImage: `url(${poster})`,
                    }}
                >
                    {!autoplay && current < 1 && !playing ? (
                        <svg
                            viewBox="0 0 503 612"
                            xmlns="http://www.w3.org/2000/svg"
                        >
                            <path
                                fill={ui?.color}
                                d="M0 608.7l503.2-302.6L0 3.5z"
                            />
                        </svg>
                    ) : (
                        <svg viewBox="0 0 425.47 402.84">
                            <g>
                                <path
                                    className="st0"
                                    fill="white"
                                    d="M138.18 396.9c-10.19-7.86-20.46-15.61-30.56-23.6-16.19-12.81-32.27-25.76-48.38-38.66-7.39-5.92-7.53-12.7-.3-18.51 24.41-19.58 48.87-39.09 73.26-58.69 4.09-3.28 8.27-5.07 13.27-2.24 3.92 2.22 5.33 5.76 5.31 10.13-.06 12.89-.02 25.78-.02 39.35h4.97c46.14 0 92.29.11 138.43-.04 41.76-.13 74.9-27.79 82.55-68.79 2.98-15.96.89-31.48-5.42-46.39-2.3-5.42-3.1-10.8-1.14-16.44 2.78-8.01 10.02-13.41 18.62-13.9 8.19-.47 16.06 4.3 19.68 12.32 7.4 16.4 10.93 33.63 10.72 51.64-.77 67.34-53.92 121.52-121.27 122.55-46.94.71-93.9.18-140.85.2h-6.3v22.05c0 4.84-.33 9.71.08 14.51.56 6.71-1.71 11.56-7.82 14.51h-4.83zM274.71 98.24c-2.04-.09-3.61-.23-5.18-.23-45.98-.01-91.96-.09-137.95.02-42 .1-75.29 27.77-82.89 68.98-2.94 15.95-.82 31.48 5.54 46.38 2.31 5.42 3 10.82.98 16.44-2.88 8.02-10.17 13.36-18.73 13.73-8.26.36-15.93-4.44-19.56-12.51-7.38-16.42-10.87-33.65-10.64-51.66.87-67.32 54.04-121.34 121.46-122.34 46.94-.7 93.9-.17 140.85-.19h6.12V25.92c0-2.42.11-4.85-.02-7.26-.27-5.08 1.12-9.31 5.98-11.67 4.95-2.4 8.95-.32 12.83 2.8 13.05 10.53 26.19 20.97 39.29 31.44 10.83 8.66 21.67 17.31 32.5 25.98 8.82 7.06 8.8 13.35-.03 20.4-23.56 18.83-47.11 37.68-70.66 56.52-1.01.81-2.02 1.6-3.05 2.38-3.54 2.68-7.34 3.23-11.31 1.1-3.75-2.01-5.52-5.26-5.52-9.51 0-11.45 0-22.91-.01-34.36v-5.5z"
                                />
                            </g>
                        </svg>
                    )}
                </div>
            ) : (
                <div
                    style={{
                        backgroundColor:
                            hexToRgb(gradient?.color, 0.75) ||
                            "rgba(0,0,0, .75)",
                    }}
                    className={styles.overlay}
                >
                    <div
                        style={{ backgroundColor: size === "one" && ui?.color }}
                        className={styles.circle}
                    >
                        {current < 60 && (
                            <span
                                style={{
                                    color:
                                        size === "one"
                                            ? gradient?.color
                                            : ui?.color,
                                }}
                            >
                                {current}
                            </span>
                        )}
                        <svg fill="rgba(0,0,0,0)" className={styles.progress}>
                            <circle
                                cx="25"
                                cy="25"
                                r="22"
                                style={{
                                    backgroundColor: "none",
                                    stroke:
                                        hexToRgb(`${ui?.color}`, 0.4) ||
                                        "white",
                                }}
                                className={styles.track}
                            />
                            <circle
                                cx="25"
                                cy="25"
                                r="22"
                                className={styles.pct}
                                style={{
                                    backgroundColor: "none",
                                    stroke: ui?.color,
                                    strokeDashoffset: `${
                                        (1 - progress / 200) *
                                        (2 * (22 / 7) * 40)
                                    }`,
                                }}
                            />
                        </svg>
                    </div>
                    <button
                        onClick={() => {
                            ref.current.plyr.togglePlay();
                        }}
                        className={styles.action}
                    >
                        {playing ? (
                            <svg
                                viewBox="0 0 503 594"
                                xmlns="http://www.w3.org/2000/svg"
                            >
                                <path
                                    fill={ui?.color}
                                    d="M2.2 2.2h174.2V594H2.2zM328.8 2.2H503V594H328.8z"
                                />
                            </svg>
                        ) : (
                            <svg
                                viewBox="0 0 503 612"
                                xmlns="http://www.w3.org/2000/svg"
                            >
                                <path
                                    fill={ui?.color}
                                    d="M0 608.7l503.2-302.6L0 3.5z"
                                />
                            </svg>
                        )}
                    </button>
                    <button
                        onClick={() => {
                            if (muted) {
                                ref.current.plyr.increaseVolume(1);
                            } else {
                                ref.current.plyr.decreaseVolume(1);
                            }
                        }}
                        className={styles.volume}
                    >
                        {muted ? (
                            <svg viewBox="0 0 510.75 511.76">
                                <path
                                    fill={ui?.color}
                                    d="M474.37 511.76h-1c-1.15-1.37-2.2-2.85-3.46-4.11-15.75-15.76-31.56-31.47-47.27-47.27-2.1-2.11-3.75-4.65-5.65-7.03-31.84 25.6-66.21 42.9-104.69 51.7-.32-.67-.59-.96-.59-1.25-.05-18.13-.14-36.25.05-54.37.01-1.32 1.92-3.19 3.38-3.83 9.26-4.07 18.98-7.21 27.89-11.92 11.17-5.91 21.7-13.02 32.04-19.33L254.62 293.9v188.32c-.42.19-.83.37-1.25.56-1.15-1.43-2.18-2.97-3.47-4.26-44.54-44.59-89.13-89.13-133.63-133.76-2.41-2.42-4.79-3.48-8.25-3.47-33.26.13-66.52.08-99.78.06-2.99 0-5.97-.16-8.96-.25V171.43c1.99-.09 3.97-.25 5.96-.25 40.56-.01 81.13-.01 121.69-.01h6.19c-1.83-2.01-2.78-3.12-3.81-4.15C87.13 124.8 44.93 82.6 2.71 40.43 1.68 39.41.44 38.6-.69 37.69v-1C11.03 24.46 22.75 12.24 34.3.19c159.26 159.26 317.32 317.32 476.51 476.5-12.04 11.58-24.24 23.32-36.44 35.07zM311.62 7.81c162.83 35.77 247.51 220.81 169.16 366.17-13.68-13.73-27.33-27.31-40.74-41.12-1.02-1.05-.8-3.88-.34-5.68 3.86-15.42 8.84-30.62 11.7-46.21 4.68-25.57 2-51.06-4.76-76.02-12.4-45.82-38.19-82.54-76.6-110.33-16.25-11.76-33.98-20.82-53.16-26.78-4.35-1.35-5.45-3.36-5.37-7.69.28-15.63.11-31.26.11-46.89V7.81z"
                                />
                                <path
                                    fill={ui?.color}
                                    d="M380.93 274.35c-6.71-6.8-12.73-12.96-18.82-19.06-15.84-15.88-31.74-31.7-47.51-47.65-1.38-1.39-2.83-3.53-2.85-5.33-.2-19.76-.13-39.53-.13-59.39 41.15 17.18 78.7 71.35 69.31 131.43zM196.95 88c18.71-18.67 38.08-37.99 57.47-57.33v114.81c-18.81-18.81-38.11-38.12-57.47-57.48z"
                                />
                            </svg>
                        ) : (
                            <svg viewBox="0 0 510.75 511.76">
                                <path
                                    fill={ui?.color}
                                    d="M0 170.93c2.98-.09 5.97-.25 8.95-.25 33.25-.02 66.51-.06 99.76.06 3.45.01 5.84-1.04 8.25-3.46 44.49-44.62 89.07-89.16 133.61-133.73 1.29-1.29 2.34-2.82 4.47-3.71v452.25c-1.55-1.43-2.87-2.57-4.1-3.8-44.57-44.55-89.15-89.08-133.63-133.71-2.67-2.67-5.3-3.83-9.1-3.82-34.08.14-68.17.09-102.25.07-1.99 0-3.98-.16-5.97-.25C0 284.04 0 227.48 0 170.93zM312.32 504.13v-35.44c0-5.99.24-11.99-.09-17.96-.2-3.65 1.11-5.22 4.53-6.29 21.61-6.77 41.38-17.18 59.28-31.04 39-30.19 64.03-69.36 73.94-117.68 17.18-83.77-20.14-167.68-94.04-211.03-12.36-7.25-26.17-12.05-39.45-17.67-3.04-1.29-4.31-2.52-4.27-6 .21-17.78.1-35.56.1-53.4 90.78 17.36 187.77 102.27 197.57 226.6 11.55 146.59-94.73 249.1-197.57 269.91z"
                                />
                                <path
                                    fill={ui?.color}
                                    d="M312.63 142.19c36.6 16.84 71.16 59.71 70.6 114.87-.57 55.95-36.91 97.18-70.6 112.3V142.19z"
                                />
                            </svg>
                        )}
                    </button>
                </div>
            )}
            <video
                ref={raptorRef as React.MutableRefObject<HTMLVideoElement>}
                className="plyr-react plyr"
            />
        </div>
    );
});

CustomPlyr.displayName = "CustomPlyr";

export default CustomPlyr;
@realamirhe
Copy link
Collaborator

What browser are you using on your iOS device? If you are using safari it seems that:

Safari browser doesn't support Ogg audio file type make sure you are not using this file type in your browser and make sure the extension of the audio file is correct.

Could you provide more info and post your founded solution to this issue too?

@c-mella
Copy link
Author

c-mella commented Aug 30, 2022

@realamirhe This is happening on both Safari and Chrome on iOS and I'm using regular mp4 files. As of now I haven't found a solution for how to make this work yet.

@chintan9
Copy link
Owner

iOS use webkit engine (safari under the hood).

Can check event is triggering or not?

Thanks for reporting.

@c-mella
Copy link
Author

c-mella commented Aug 30, 2022

Ok so I ran a test where i set the function to either increase or decrease based on the volume coming from the player and no matter what happened it always defaulted to decreaseVolume. This leads me to believe that the problem is in the connection between the player ref and the controls. For reference the togglePlay function will always fire and work properly. Same as before this worked fine on desktop but on mobile it's not working.

here's the function code i used for the test

onClick={() => {
  if (ref?.current?.plyr?.volume < 1) {
      console.log("event fired to increase");
      setMuted(false);
      ref.current.plyr.increaseVolume(1);
  } else {
      console.log("event fired to decrease");
      setMuted(true);
      ref.current.plyr.decreaseVolume(1);
  }
}}

@c-mella
Copy link
Author

c-mella commented Aug 30, 2022

I also ran a test where I used the custom metric of "muted" to set the function but in that case even though the event registered and printed "event fired to decrease", the audio would be stuck at 1.

Here's the code for context:

onClick={() => {
  if (muted) {
      console.log("event fired to increase");
      setMuted(false);
      ref.current.plyr.increaseVolume(1);
  } else {
      console.log("event fired to decrease");
      setMuted(true);
      ref.current.plyr.decreaseVolume(1);
  }
}}

@realamirhe
Copy link
Collaborator

Thanks for the detailed test @c-mella; could you put log (alert) down on the plyr increaseVolume?
Or even simpler could you make the same functionality using plyr in codepen and test it on safari?
Here is a simple starter kit (codepen)

@realamirhe
Copy link
Collaborator

I also ran a test where I used the custom metric of "muted" to set the function but in that case even though the event registered and printed "event fired to decrease", the audio would be stuck at 1.

Here's the code for context:

onClick={() => {
  if (muted) {
      console.log("event fired to increase");
      setMuted(false);
      ref.current.plyr.increaseVolume(1);
  } else {
      console.log("event fired to decrease");
      setMuted(true);
      ref.current.plyr.decreaseVolume(1);
  }
}}

How and when do you grab the muted property?

@c-mella
Copy link
Author

c-mella commented Aug 31, 2022

How and when do you grab the muted property?

I'll work on getting you that detailed CodePen test, in the meantime here's a breakdown of the muted property.

In the original code snippet I set it in using useState with a default value of true and then update it using useEffect whenever there is a change in volume (This work on Desktop but not mobile)

In the subsequent examples I was setting again setting it using useState but then updating it manually whenever the button is clicked. In this case the button would update the value and fire the console log but the decreaseVolume action wouldn't fire for some reason. Meaning that the button would change but the volume would remain at 1

@realamirhe
Copy link
Collaborator

Hmm, that's weird, in case you don't get an error in calling the function it means that you get some proper instance of Plyr instead of the default proxy.

I'm waiting for your codepen test, so we can trace down the issue more properly.
Thanks for your effort @c-mella.

@c-mella
Copy link
Author

c-mella commented Aug 31, 2022

I removed a lot of the dynamic aspects of the code and replaced it with hard coded values in order to make the test work but here is the sample code for you to review.

https://codepen.io/c-mella/pen/JjvPRjM

Thanks again for you help

@c-mella
Copy link
Author

c-mella commented Sep 13, 2022

Hi, just wanted to see if you'd had a chance to review? I just went in and revised the pen with a new video because I realized the previous one had very subtle audio so it wouldnt be easy to test.

@realamirhe
Copy link
Collaborator

oh sorry for delay @c-mella,

I might miss the notification or didn't get one, please directly mention me if I didn't respond.

I'm going to review it today.
Thanks for your help ❤️

@realamirhe
Copy link
Collaborator

Hey @c-mella despite extra unnecessary re-render the code seems to find at the code level, and functional in all available platforms for me.

I changed it a little bit by adding the observed variable at the top of the video for testing on mobile (iOS)

codepen

@c-mella
Copy link
Author

c-mella commented Sep 15, 2022

@realamirhe Thanks again for looking into it, I'm not sure what the issue is but I and my teammates are still seeing the video as muted no matter how much we click the unmute button. I'm not sure if this helps but I'm on an iPhone 13 running iOS 15.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants