Jump to content
This topic contains 43 posts. A summary containing the most significant posts is available

Featured Replies

  • Community Expert
  • Management
 

I won’t bring this up ever again.

Frustrated World Cup GIF

Yes, we know the PWA needs work. We will make it better, but we really need to get 5.0.0 out. I know it’s very important to you, and I understand why but there are around 200 very important things that 200 people want adding in before final, and we just can’t do it all.

Lets get 5.0.0 out and then we can really get into building on existing features.

  • Author

I consider this navigation issue on the iOS PWA a bug and not a feature.

I guess you don’t agree and are not willing to fix this before GA.

I really will shut up about this now, but I am extremely frustrated by not getting the UI issues on iPhone addressed for non-technical users before v5 release which is very very important to me for my new v5 based site.

I’ll probably have to fix this myself, not use the PWA for users that Add To Home Screen or delay opening the site until this fix is in a point release. Probably I will just fix myself.

  • Community Expert
 

I consider this navigation issue on the iOS PWA a bug and not a feature.

I guess you don’t agree and are not willing to fix this before GA.

I really will shut up about this now, but I am extremely frustrated by not getting the UI issues on iPhone addressed for non-technical users before v5 release which is very very important to me for my new v5 based site.

I’ll probably have to fix this myself, not use the PWA for users that Add To Home Screen or delay opening the site until this fix is in a point release. Probably I will just fix myself.

We fully understand you are frustrated. We hear you.

  • Community Expert
  • Management

It’s not a bug in the sense that there is a refresh function that doesn’t work.

I agree that there needs to be a refresh, I just disagree that it’s a few minutes work to do properly (there’s obviously the JS to allow the pull down, then fire a reload() and then the CSS to style it up) but I’d also like to add swipe gestures to allow you to navigate backwards and forwards when on a page as you would on an app.

  • Author
 

It’s not a bug in the sense that there is a refresh function that doesn’t work.

I agree that there needs to be a refresh, I just disagree that it’s a few minutes work to do properly (there’s obviously the JS to allow the pull down, then fire a reload() and then the CSS to style it up) but I’d also like to add swipe gestures to allow you to navigate backwards and forwards when on a page as you would on an app.

Swipe back and forward already work in the PWA, even from the beginning of PWA support. The only thing missing is the pull to refresh and there are a number of JS code snippets you could use to add pull to refresh to the JS. I don’t think you have to actually code anything.

I’ll do a quick search to find you some links.

  • Author

Here is Claude 3.5 Sonnet’s answer to adding “pull to refresh” to a PWA on iOS.

Claude on adding pull to refresh

Yes! Here's a JavaScript solution to implement pull-to-refresh functionality in Progressive Web Apps on iOS. This code uses the touchstart, touchmove, and touchend events to create a native-feeling pull-to-refresh experience:

```javascript

class PullToRefresh {

constructor(options) {

this.options = {

target: document.querySelector("body"),

threshold: 60, // Required pull distance in pixels

onRefresh: () => location.reload(),

...options,

};

this.startY = 0;

this.currentY = 0;

this.dragging = false;

this.refreshing = false;

this.init();

}

init() {

// Create refresh indicator element

this.indicator = document.createElement("div");

this.indicator.style.cssText = `

position: fixed;

top: 0;

left: 0;

right: 0;

height: 50px;

display: flex;

align-items: center;

justify-content: center;

transform: translateY(-100%);

transition: transform 0.2s;

`;

document.body.prepend(this.indicator);

// Add event listeners

this.options.target.addEventListener("touchstart", (e) =>

this.onTouchStart(e)

);

this.options.target.addEventListener("touchmove", (e) => this.onTouchMove(e));

this.options.target.addEventListener("touchend", (e) => this.onTouchEnd(e));

}

onTouchStart(e) {

if (window.scrollY === 0 && !this.refreshing) {

this.dragging = true;

this.startY = e.touches[0].pageY;

}

}

onTouchMove(e) {

if (!this.dragging) return;

this.currentY = e.touches[0].pageY;

const distance = this.currentY - this.startY;

if (distance > 0) {

e.preventDefault();

const pullDistance = Math.min(distance * 0.5, this.options.threshold);

this.indicator.style.transform = translateY(${pullDistance}px);

this.indicator.textContent = pullDistance >= this.options.threshold

? "Release to refresh"

: "Pull to refresh";

}

}

onTouchEnd() {

if (!this.dragging) return;

this.dragging = false;

const distance = this.currentY - this.startY;

if (distance >= this.options.threshold) {

this.refreshing = true;

this.indicator.textContent = "Refreshing...";

this.options.onRefresh().finally(() => {

this.refreshing = false;

this.indicator.style.transform = "translateY(-100%)";

});

} else {

this.indicator.style.transform = "translateY(-100%)";

}

}

}

// Usage example

const ptr = new PullToRefresh({

target: document.querySelector("#content"),

onRefresh: async () => {

// Your refresh logic here

await new Promise((resolve) => setTimeout(resolve, 1500));

location.reload();

},

});

```

To use this in your PWA:

1. Add the code to your project

2. Initialize it with your desired target element and refresh callback

3. Add the following to your manifest.json to enable pull-to-refresh:

```json

{

"display": "standalone",

"apple-mobile-web-app-capable": "yes"

}

```

Key features of this implementation:

- Works on iOS Safari and when installed as a PWA

- Smooth animation with native feel

- Customizable threshold and refresh callback

- Visual indicator with status messages

- Prevents default scroll behavior when pulling

- Handles edge cases and cleanup

You can customize the appearance of the refresh indicator by modifying the CSS in the init() method. You might also want to add a loading spinner or other visual feedback during the refresh operation.

Note that this is a basic implementation - you might want to add:

1. Better error handling

2. Animation for the refresh indicator

3. Custom styling to match your app's design

4. Support for different pull distances

5. Better accessibility features

Remember that while this works well for iOS, Android typically has built-in pull-to-refresh functionality that you might want to preserve, so you may want to add platform detection and only enable this for iOS devices.

  • Author

Here is Claude’s response to adding pull to refresh in PWAs on iOS. Don’t know if this all works but Claude usually writes good JS for me.

Here's the updated version of the PullToRefresh class that only enables the functionality for iOS devices:

```javascript
class PullToRefresh {
  constructor(options) {
    // First check if we should initialize on this platform
    if (!this.shouldInitialize()) {
      return;
    }

    this.options = {
      target: document.querySelector("body"),
      threshold: 60,
      onRefresh: () => location.reload(),
      ...options,
    };

    this.startY = 0;
    this.currentY = 0;
    this.dragging = false;
    this.refreshing = false;

    this.init();
  }

  shouldInitialize() {
    // Check for iOS devices
    const isIOS =
      /iPad|iPhone|iPod/.test(navigator.userAgent) ||
      (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);

    // Check if it's running as a PWA
    const isStandalone =
      window.navigator.standalone ||
      window.matchMedia("(display-mode: standalone)").matches;

    // Only initialize for iOS devices running as PWA
    return isIOS && isStandalone;
  }

  init() {
    // Create refresh indicator element
    this.indicator = document.createElement("div");
    this.indicator.setAttribute("aria-label", "Pull to refresh indicator");
    this.indicator.style.cssText = `
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      height: 50px;
      display: flex;
      align-items: center;
      justify-content: center;
      transform: translateY(-100%);
      transition: transform 0.2s;
      background-color: #f0f0f0;
      z-index: 9999;
      font-family: -apple-system, system-ui, sans-serif;
    `;
    document.body.prepend(this.indicator);

    // Create loading spinner
    this.spinner = document.createElement("div");
    this.spinner.style.cssText = `
      display: none;
      width: 20px;
      height: 20px;
      margin-right: 10px;
      border: 2px solid #007AFF;
      border-top-color: transparent;
      border-radius: 50%;
      animation: spin 1s linear infinite;
    `;

    // Add spinner animation
    const style = document.createElement("style");
    style.textContent = `
      @keyframes spin {
        to { transform: rotate(360deg); }
      }
    `;
    document.head.appendChild(style);

    this.indicator.appendChild(this.spinner);

    // Add text element
    this.textElement = document.createElement("span");
    this.indicator.appendChild(this.textElement);

    // Add event listeners with passive: false for better performance
    this.options.target.addEventListener(
      "touchstart",
      (e) => this.onTouchStart(e),
      { passive: false }
    );
    this.options.target.addEventListener(
      "touchmove",
      (e) => this.onTouchMove(e),
      { passive: false }
    );
    this.options.target.addEventListener("touchend", (e) => this.onTouchEnd(e), {
      passive: false,
    });
  }

  updateUI(distance) {
    const progress = (distance / this.options.threshold) * 100;
    this.spinner.style.transform = `rotate(${progress * 3.6}deg)`;
    this.spinner.style.display = "block";
    this.textElement.textContent =
      distance >= this.options.threshold ? "Release to refresh" : "Pull to refresh";
  }

  onTouchStart(e) {
    if (window.scrollY === 0 && !this.refreshing) {
      this.dragging = true;
      this.startY = e.touches[0].pageY;
    }
  }

  onTouchMove(e) {
    if (!this.dragging) return;

    this.currentY = e.touches[0].pageY;
    const distance = this.currentY - this.startY;

    if (distance > 0) {
      e.preventDefault();
      const pullDistance = Math.min(distance * 0.5, this.options.threshold);
      this.indicator.style.transform = `translateY(${pullDistance}px)`;
      this.updateUI(pullDistance);
    }
  }

  onTouchEnd() {
    if (!this.dragging) return;
    this.dragging = false;

    const distance = this.currentY - this.startY;
    if (distance >= this.options.threshold) {
      this.refreshing = true;
      this.spinner.style.display = "block";
      this.textElement.textContent = "Refreshing...";
      this.spinner.style.animation = "spin 1s linear infinite";

      this.options.onRefresh()
        .finally(() => {
          this.refreshing = false;
          this.indicator.style.transform = "translateY(-100%)";
          this.spinner.style.animation = "none";
        })
        .catch((error) => {
          console.error("Refresh failed:", error);
          this.textElement.textContent = "Refresh failed";
          setTimeout(() => {
            this.indicator.style.transform = "translateY(-100%)";
          }, 1000);
        });
    } else {
      this.indicator.style.transform = "translateY(-100%)";
    }
  }

  // Method to manually destroy the instance
  destroy() {
    if (this.indicator) {
      this.indicator.remove();
    }
    if (this.options.target) {
      this.options.target.removeEventListener("touchstart", this.onTouchStart);
      this.options.target.removeEventListener("touchmove", this.onTouchMove);
      this.options.target.removeEventListener("touchend", this.onTouchEnd);
    }
  }
}

// Usage example
const ptr = new PullToRefresh({
  target: document.querySelector("#content"),
  onRefresh: async () => {
    try {
      // Your refresh logic here
      await new Promise((resolve) => setTimeout(resolve, 1500));
      location.reload();
    } catch (error) {
      console.error("Refresh failed:", error);
      throw error; // Rethrow to trigger error handling in the class
    }
  },
});
```

This updated version includes:

1. Platform detection through `shouldInitialize()` method
2. Better iOS detection including newer iPads
3. PWA (standalone mode) detection
4. Improved visual feedback with a spinning loader
5. Better error handling
6. Improved accessibility
7. Performance optimizations with passive event listeners
8. Cleanup method through `destroy()`
9. More iOS-native feeling styling

To use it:

```javascript
// Initialize
const ptr = new PullToRefresh({
  target: document.querySelector("#content"),
  threshold: 60, // Optional: customize pull distance
  onRefresh: async () => {
    // Your custom refresh logic
    await fetchNewData();
  },
});

// Later, if needed, cleanup
ptr.destroy();
```

The pull-to-refresh will now only initialize on iOS devices running as a PWA. On other platforms, it will silently do nothing, allowing the native pull-to-refresh behavior (if any) to work as expected.

Remember to still include the appropriate manifest.json settings:

```json
{
  "display": "standalone",
  "apple-mobile-web-app-capable": "yes"
}
```

This implementation provides a more native-feeling experience for iOS PWA users while not interfering with other platforms' built-in behaviors.
  • Community Expert
  • Management

I’m confident that we have the skill set to write fairly basic JS without AI help so it’s not a skill issue. It’s a priority and release issue.

  • Community Expert

Can I have a link for that AI that can write code? I need it to write some code for me.

  • Author
 

Can I have a link for that AI that can write code? I need it to write some code for me.

I use Claude 3.5 Sonnet via Theo’s new chat SaaS which provides a fast basic chat wrapper around several LLMs. There are tons of these kind of site so you would have no problem finding one that can help you with any coding questions.

Note, that if you want to have AI assist you in actual coding, use CoPilot in your favorite PHP editor like VS Code or the fantastic PHPStorm.

Here is a link to Theo’s chat SaaS:

https://t3.chat

  • Author
 

I’m confident that we have the skill set to write fairly basic JS without AI help so it’s not a skill issue. It’s a priority and release issue.

I didn’t mean to imply that your devs don’t have the skills to code.

I recently started using AI as my coding assistant both through Claude and Copilot and it has made me 10x faster at producing new code so small tasks like implementing pull-to-refresh which now only takes an hour or so. Prior to Claude this would have taken a day for me to code and even longer to get the bugs out. So, I would usually reach for a JS or PHP package to accelerate adding features to my code.

Anyway, AI makes good programmers code way faster in my recent experience.

Now that I thought of using AI to add missing features to v3, I’m feeling a lot better about your decision to not make the mobile UX a good one for non-technical users in the first release. I still do not have the skills to debug JS, but at least I have Claude to help me now.

  • Author

BTW, if you haven’t tried Deepseek R1 yet, you should. It shows you in minute detail about it’s reasoning in coming up with a response.

So, even if it gets the answers wrong, you can learn what you need to solve the problem the first time and not miss thinking about edge cases.

I have been running R1 prompts first before using Claude to get Claude to write me code. I’ve only been using R1 for a couple of days now, but so far, it has helped me to think about coding problems in a way I wasn’t before.

  • Community Expert
  • Management

We’re not going to use AI to code. I use GitHubs “AI” autocomplete in PHPStorm as it’s linked to our GitHub repository so it makes useful suggestions, but we’re not going to add code we don’t fully understand and is not structured in a way we can expand on it.

Back on PWA, @Ehren has polished a little to ensure the bottom bar isn’t masked by the iPhone UI, and he’s added a refresh button if the PWA mode is minimal or stand alone.

CleanShot 2025-01-28 at 12.33.34@2x.png

  • Author
 

Back on PWA, @Ehren has polished a little to ensure the bottom bar isn’t masked by the iPhone UI, and he’s added a refresh button if the PWA mode is minimal or stand alone.

CleanShot 2025-01-28 at 12.33.34@2x.png

This is fantastic news. Thanks for giving the PWA a little ❤️ before the GA ships. There are still a few places in the UI where some automatic refreshes are missing (at least on my fast iPhone). These might be timing issues with JavaScript so sometimes works but most times don’t work for me. Also, push notification badges don’t show on the PWA icon, but this has been a bug since you first added push notifications to the suite and has never been fixed.

Other than that, v5 is a huge improvement on mobile and using the PWA.

Thanks again for the reload button in the PWA.

  • Author
 

We’re not going to use AI to code. I use GitHubs “AI” autocomplete in PHPStorm as it’s linked to our GitHub repository so it makes useful suggestions, but we’re not going to add code we don’t fully understand and is not structured in a way we can expand on it.

BTW, I had the same opinion about coding and AI until I started a new PHP side-project from scratch a couple of weeks ago.

But these past few weeks using Claude 3.5 Sonnet for PHP has been completely eye opening to me. For example, Claude has helped greatly in my coding of a PDO DB class that is full featured and totally ready for production use. Also, I’ve had tremendous success with Repositories and DTOs/Entities in a way that are also of very high quality (complete with very good data validation rules specific to my database). I’m currently working on a cli for my app and adding RabbitMQ for sending data crud operations to the admin server. Anyway, I would not be able to have coded all this in such a short time without Claude’s help.

Another exciting development in AI for software development is the release of Deepseek R1 last week.

R1 is not much of a PHP coder (in my limited experience), but the fact that it is the first reasoning model I know of that shows it’s thinking before it generates a response is a real game changer for me as a developer. I can see all the possibilities that R1 considered and often abandoned and this makes me consider way more things as I create new code that weren’t on my mind before asking the AI for it’s thoughts.

The last question I almost always ask the AI before ending a chat, is what more can it do for using this code in production. This often lists 5 or 10 areas that should be worked on further and then I continue the chat with having the AI prototyping a solution that addresses my particular needs and applying the solutions to the code it has written before.

Sure, the code produced by Claude is usually prototype quality (sometimes it is perfect for me) but I know enough about writing good well designed code to be able to rework/refactor the code for production use. This has cut down the time to add a feature from a week or two to a day or two and my code is of higher quality than without the AI’s help.

I’m also starting to explore having AI help me in writing tests for my PHP app. Pest 2.0, the new replacement for PHPUnit is a great test runner that adds code coverage analysis and many other features that help give you confidence that you have enough tests to properly take your code to production and to have the confidence that future changes won’t introduce regressions into the rest of the app.

Edited by KT Walrus

 

Back on PWA, @Ehren has polished a little to ensure the bottom bar isn’t masked by the iPhone UI

Any chance whilst he’s there, moving the bottom bar down 1px on the mobile website.

Seeing text flash through that tiny gap when scrolling is triggering my OCD something crazy. I’d post it as a bug but I’d be laughed out of there 🤣🤣

IMG_1972.jpeg

Edited by Day_

  • Community Expert

That looks like some sub-pixel rendering. It seems to only happen when the URL bar is positioned at the top of Safari. I'll fix it this afternoon. 🤝

Recently Browsing 0

  • No registered users viewing this page.