Skip to content

Commit

Permalink
Merge pull request #4 from DKeken/bridges
Browse files Browse the repository at this point in the history
Improvements: add haptic-feedback and cloud-storage bindings
  • Loading branch information
DKeken authored Nov 3, 2024
2 parents a1aacd9 + ed9e601 commit 8ecb30a
Show file tree
Hide file tree
Showing 12 changed files with 486 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/tiny-berries-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effector-telegram-mini-app": minor
---

Improvements: add haptic-feedback and cloud-storage bindings
53 changes: 53 additions & 0 deletions .github/workflows/deploy-demo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Deploy Demo App

on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: "18"
cache: "npm"

- name: Install Dependencies
run: npm ci

- name: Build
run: npm run build
working-directory: ./apps/example
env:
VITE_BASE_URL: "/${{ github.event.repository.name }}"

- name: Setup Pages
uses: actions/configure-pages@v3

- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: "./apps/example/dist"

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
28 changes: 28 additions & 0 deletions .github/workflows/merge-with-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Merge with Tests

on:
pull_request:
branches:
- main
push:
branches:
- main

jobs:
test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3

- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: 1.1.28

- name: Install dependencies
run: bun install

- name: Build packages
run: bun run ci:packages:rollup
27 changes: 7 additions & 20 deletions apps/example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,22 @@
import {
$initData,
$initDataUnsafe,
$telegramWebApp,
} from "effector-telegram-mini-app";
import { useGate, useUnit } from "effector-react";
import { PageStartGate, $viewportChanged, $themeChanged } from "./model";
import { InputForm } from "./components/InputForm";
import { UserInfo } from "./components/UserInfo";
import { HapticFeedback } from "./components/HapticFeedback";

function App() {
useGate(PageStartGate);

const {
telegramWebApp,
initData,
initDataUnsafe,
viewportChanged,
themeChanged,
} = useUnit({
telegramWebApp: $telegramWebApp,
initData: $initData,
initDataUnsafe: $initDataUnsafe,
const { viewportChanged, themeChanged } = useUnit({
viewportChanged: $viewportChanged,
themeChanged: $themeChanged,
});

console.log(viewportChanged, themeChanged);

return (
<div>
{initDataUnsafe &&
"user" in initDataUnsafe &&
initDataUnsafe?.user.username}
<HapticFeedback />
<InputForm />
<UserInfo />
</div>
);
}
Expand Down
81 changes: 81 additions & 0 deletions apps/example/src/components/HapticFeedback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { hapticFeedback } from "effector-telegram-mini-app";

export function HapticFeedback() {
const handleButtonPress = () => {
// Light impact for button press feedback
hapticFeedback.impactOccurred("light");
};

const handleSuccess = () => {
// Success notification for completed actions
hapticFeedback.notificationOccurred("success");
};

const handleError = () => {
// Error notification for failed actions
hapticFeedback.notificationOccurred("error");
};

const handleWarning = () => {
// Warning notification for important alerts
hapticFeedback.notificationOccurred("warning");
};

const handleSliderChange = () => {
// Soft impact for continuous interactions
hapticFeedback.impactOccurred("soft");
};

const handleToggle = () => {
// Selection changed for toggle switches
hapticFeedback.selectionChanged();
};

const handleHeavyAction = () => {
// Heavy impact for significant actions
hapticFeedback.impactOccurred("heavy");
};

return (
<div>
<h2>Haptic Feedback Demo</h2>

<div style={{ marginBottom: 20 }}>
<h3>Basic Interactions</h3>
<button onClick={handleButtonPress}>
Button Press (Light Impact)
</button>
<button onClick={handleToggle}>
Toggle Switch
</button>
</div>

<div style={{ marginBottom: 20 }}>
<h3>Notifications</h3>
<button onClick={handleSuccess}>
Success Notification
</button>
<button onClick={handleError}>
Error Notification
</button>
<button onClick={handleWarning}>
Warning Notification
</button>
</div>

<div style={{ marginBottom: 20 }}>
<h3>Special Actions</h3>
<button onClick={handleHeavyAction}>
Heavy Impact
</button>
<input
type="range"
onChange={handleSliderChange}
min="0"
max="100"
/>
<div>Slider with Soft Impact</div>
</div>
</div>
);
}
145 changes: 145 additions & 0 deletions apps/example/src/components/InputForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { useState, useEffect } from "react";
import { cloudStorage } from "effector-telegram-mini-app";

export function InputForm() {
const [inputValue, setInputValue] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [savedValues, setSavedValues] = useState<string[]>([]);

useEffect(() => {
loadSavedValues();
loadInitialValue();
}, []);

const loadSavedValues = async () => {
try {
const keys = await cloudStorage.getKeys();
const values = await cloudStorage.getItems(keys);
const nonNullValues = values.filter((v): v is string => v !== null);
setSavedValues(nonNullValues);
} catch (error) {
console.error("Failed to load saved values:", error);
}
};

const loadInitialValue = async () => {
setIsLoading(true);
setError(null);
try {
const value = await cloudStorage.getItem("input-value");
if (value) {
setInputValue(value);
}
} catch (error) {
setError("Failed to load initial value");
console.error("Failed to load initial value:", error);
} finally {
setIsLoading(false);
}
};

const handleSave = async () => {
if (!inputValue.trim()) {
setError("Please enter some text before saving");
return;
}

setIsLoading(true);
setError(null);
try {
await cloudStorage.setItem({
key: "input-value",
value: inputValue,
});
await loadSavedValues();
setError("Saved successfully!");
} catch (error) {
setError("Failed to save");
console.error("Failed to save:", error);
} finally {
setIsLoading(false);
}
};

const handleLoad = async () => {
setIsLoading(true);
setError(null);
try {
const value = await cloudStorage.getItem("input-value");
if (value) {
setInputValue(value);
} else {
setError("No saved value found");
}
} catch (error) {
setError("Failed to load");
console.error("Failed to load:", error);
} finally {
setIsLoading(false);
}
};

const handleClear = async () => {
setIsLoading(true);
setError(null);
try {
await cloudStorage.removeItem("input-value");
setInputValue("");
await loadSavedValues();
setError("Cleared successfully!");
} catch (error) {
setError("Failed to clear");
console.error("Failed to clear:", error);
} finally {
setIsLoading(false);
}
};

return (
<div>
<div style={{ marginBottom: 10 }}>
{error && (
<div style={{ color: error.includes("success") ? "green" : "red" }}>
{error}
</div>
)}
</div>

<input
type="text"
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
setError(null);
}}
placeholder="Enter text"
disabled={isLoading}
style={{ marginBottom: 10 }}
/>

<div>
<button onClick={handleSave} disabled={isLoading || !inputValue.trim()}>
{isLoading ? "Saving..." : "Save"}
</button>
<button onClick={handleLoad} disabled={isLoading}>
{isLoading ? "Loading..." : "Load"}
</button>
<button onClick={handleClear} disabled={isLoading || !inputValue}>
{isLoading ? "Clearing..." : "Clear"}
</button>
</div>

{savedValues.length > 0 && (
<div style={{ marginTop: 10 }}>
<h4>Saved values:</h4>
<ul>
{savedValues.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
</div>
)}
</div>
);
}
14 changes: 14 additions & 0 deletions apps/example/src/components/UserInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useUnit } from "effector-react";
import { $initDataUnsafe } from "effector-telegram-mini-app";

export function UserInfo() {
const initDataUnsafe = useUnit($initDataUnsafe);

return (
<div>
{initDataUnsafe &&
"user" in initDataUnsafe &&
initDataUnsafe?.user.username}
</div>
);
}
Loading

0 comments on commit 8ecb30a

Please sign in to comment.