Compare commits

..

79 Commits

Author SHA1 Message Date
5304ab1d6d iOS Notification Service 2026-05-07 14:07:02 -04:00
metacryst
69469fa2ad Swipe navigation library 2026-04-19 21:56:09 -05:00
metacryst
142d4a3fc3 Allow fetching from multiple sources 2026-04-16 18:27:24 -05:00
metacryst
0bb544e96c update version, incorporate error screen 2026-04-10 03:19:22 -05:00
metacryst
a104b43b24 error handling for no internet 2026-04-09 07:42:56 -05:00
metacryst
8d5a63b262 fetch everything remotely 2026-04-09 06:22:32 -05:00
14c885a60f Animation conflict fix 2026-04-06 16:47:07 -04:00
422e89caee Fixed serverFunctions naming issue 2026-04-06 12:25:47 -04:00
d0df371a3c Sidebar controls for calendar
- Added toggle for switching between day/week/month (calendar) in the sidebar
- Added a "lightdivider" shared.css var
2026-04-04 20:32:40 -04:00
metacryst
a1803a6f4a Merge branch 'main' of https://git.sun.museum/sam/ForumMobile 2026-04-04 01:41:37 -05:00
metacryst
3fe1386415 remote url changed 2026-04-04 01:41:35 -05:00
a306f0732c Removed /db from getImageURL 2026-04-03 19:23:07 -04:00
027ae4d130 Added CSS var and /@server on viite config 2026-04-03 13:56:17 -04:00
metacryst
2592137fa3 quill updates, signup comment 2026-03-31 07:08:07 -05:00
metacryst
8279a81dc9 updating quill 2026-03-28 02:24:04 -05:00
metacryst
2faa9e740e xcode changes for release 2026-03-27 06:03:28 -05:00
metacryst
a589977015 notifications setting 2026-03-26 07:44:51 -05:00
metacryst
06e2fabe81 push notifications working 2026-03-26 05:56:14 -05:00
metacryst
d107d68bcc push notifications working, server functions coming from backend 2026-03-26 02:32:59 -05:00
metacryst
472e69d3c0 adding error handling for signup, moving delete account button in sidebar to bottom 2026-03-25 23:41:45 -05:00
1c61a4d202 Delete account in sidebar 2026-03-24 16:39:53 -04:00
40b0855ca5 EnterCode Signup working
- After fetching join code in EnterCode(), it sets networkId attribute on "signup-" (Signup.js)
- Signup.js then includes the attribute in the call body
- Modified all calls to /signout, /profile, /login, /signup to be prefixed by '/auth'
- Added '/auth' to vite config file
- Modified final "else if" statement in .attr in quill.js to return `this.getAttribute(arg1)` instead of `this.getAttribute("")`
2026-03-24 15:48:23 -04:00
124066da59 Apps load from server
- Deleted /Jobs, /Announcements, /Events, /People
- Commented out Forum and ForumPanel
- Deleted /components/SearchBar, /components/LoadingCircle, /components/AddButton
2026-03-24 14:51:44 -04:00
metacryst
f3aceb69af accepting code for auth page 2026-03-24 10:39:57 -05:00
metacryst
a87d521a4f improving authpage 2026-03-24 06:07:29 -05:00
metacryst
c5b71add07 custom app rendering 2026-03-24 04:16:00 -05:00
metacryst
881c9408b6 new announcements page, adding searchbar and message input. 2026-03-23 05:26:55 -05:00
metacryst
35f0fe3654 smaller font for header, announcements full size always 2026-03-21 06:54:53 -05:00
metacryst
21b7b0a252 Sidebar fully functional 2026-03-21 03:10:50 -05:00
metacryst
1c6f12c210 preliminary sidebar fixes 2026-03-20 17:16:05 -05:00
56f7c7d3a3 Sidebar + Profile changes
- Sidebar displays profile photo and name, can be tapped on
- Profile now has "Tap to edit" button on photo
2026-03-20 14:13:14 -04:00
c7ddb02ac1 Sidebar content moved to new draggable sidebar 2026-03-20 13:43:11 -04:00
63fbab34ce Announcements page
- Updated handlers.js
- Repurposed Forum and ForumPanel for Announcements
- Adjusted styling
- Commented out long drag logs
2026-03-20 12:36:23 -04:00
metacryst
8fad5d7717 fix events icon, misc errors, new sidebar started 2026-03-20 09:27:56 -05:00
metacryst
41a9c9d269 adding state() to quill, after-request error handling to login 2026-03-20 00:36:33 -05:00
dd1ec2c374 Upload image
- Image upload works on profile
- added multer into package.json for handling image files
- files are saved under /db/images/users/user-id/profile.ext
2026-03-19 20:25:23 -04:00
58589c56dd Add event + add job form
- Modified handlers to catch errors
- Added placeholder "No location added", etc. messages to Job/Event cards
- Added EventForm.js and JobForm.js for adding
- EventForm and JobForm are animated to slide up from bottom
- Modified openProfile/closeProfile logic
- Fixed SidebarItem().onClick() firing twice bug (switched to .onTap)
- Profile is now animated to slide up from the bottom
2026-03-19 15:32:51 -04:00
metacryst
8dd2312aa0 fixing logout, people not scrolling, sidebar being too short 2026-03-19 08:54:53 -05:00
metacryst
5a56dfa051 improving styling, fixing bugs with profile, login error handling 2026-03-19 07:41:18 -05:00
3a5214ed45 Added missing Profile element
- User's "joined" info
2026-03-19 00:19:41 -04:00
72f0518f9d Profile + Edit Bio + Logout + styling
- Added handler for editing bio to handlers.js
- Added openProfile() and closeProfile() buttons in AppWindowContainer (Profile page is global)
- Added Logout and Profile functionality to Sidebar.js
  - SidebarItem(text).onClick() fires twice, unable to resolve
- Adjust Login page styling
- Added onLogout() to index.js (removes auth_token)
- Added Profile.js, displays user's profile picture (placeholder), name, and bio. User can edit bio.
- Added removeAuthToken() to util.js
- Added /signout to vite config
2026-03-19 00:14:29 -04:00
ede464fb0d Search in Events/Jobs
- SearchBar now dispatches 'jobsearch' or 'eventsearch' event whenever the user submits a search query
- Jobs/Events will then receive searchText to do general search
- Fixed bug where Jobs/Events wouldn't scroll anymore
2026-03-18 20:11:08 -04:00
2082e0c7bc Signup/Login + styling adjustments
- Modified SearchBar styling
- Modified TopBar to display blank circle if the user has no networks (previously missing image icon)
- Refactored Login into AuthPage.js
- AuthPage contains a tab selector for switching between Signup and Login
- Both Login/Signup send the request and either receive an auth_token or an error message
- If auth_token, user will be logged in as usual, in both cases
- Signup validates user input before sending request
- Added /signup target in vite config file
2026-03-18 17:36:03 -04:00
metacryst
d1e4814593 successfully connecting to prod 2026-03-17 07:00:27 -05:00
metacryst
530ea7da89 app icon, styling, light mode, top bar component 2026-03-17 05:33:28 -05:00
metacryst
5903bafee5 better styling 2026-03-16 23:55:56 -05:00
8452841460 Styled Jobs and Events like Figma mockups
- Modified some darkmode css values to match those on the figma
- Fixed misnamed calls to var(--darkaccent) from var(--accentdark)
- Commented out trash/delete button on EventCard and JobCard for now
- Hid scrollbar on Events/Jobs
- Fixed mis-centered People list
- Fixed colors in searchbar in events/jobs
2026-03-16 21:40:03 -04:00
metacryst
69b359d9a1 making login work for prod 2026-03-16 07:56:40 -05:00
metacryst
a626abe1c3 improve styling, fix bottom bar underline bug 2026-03-16 01:09:48 -05:00
834d5e763e Connected DB to events/jobs + more
- Modified handlers.js to be the same as on frm.so
- Added --trash-src to shared.css
- Modified Event and Job cards to include a trash icon for deleting
- Deleting works, it just does not smoothly re-render yet
- Adjusted visual bug on Events/Jobs where the contents of the AppWindow would overflow vertically. They now scroll and the title/search bar remain fixed.
- Refactored part of People.js into PeopleCard.js
2026-03-15 19:45:04 -04:00
dde27f9b31 Merge branch 'main' of https://git.sun.museum/sam/ForumMobile 2026-03-15 16:00:38 -04:00
0d5e68188d Set up bridge folder + changes to Events/Jobs
- Copied bridge folder from frm.so
- All handlers in handlers.js added from frm.so
- Modified Events and Jobs pages' default events/jobs to model data structure from SQL/server
- Set up getJobs(), checkForUpdates() on both Events/Jobs to fetch new items and update when needed
2026-03-15 16:00:37 -04:00
metacryst
fc2d9c2bc9 making ready for production, fixing app menu spacing, some better icons 2026-03-15 08:13:44 -05:00
cb11d68fa7 SearchBar component + Jobs changes + Events changes
- Added SearchBar.js to components (repeated in Jobs and Events)
- Set .position("relative") on AppWindow for LoadingCircles and future fixed/absolute elements
- Refactored Events.js and Jobs.js
- Added unfinished JobForm.js for editing/adding Jobs, preliminary designs/setup
2026-03-14 23:26:15 -04:00
metacryst
07f431e2a3 merge 2026-03-14 16:53:38 -05:00
e85ffc66f8 Jobs + Events Pages
- Added Jobs/Events pages, need to set up with backend once complete
- Added missing icons and fixed incorrect icons
- Made AppMenu icons highlight when selected
- Removed Settings from AppMenu
- Fixed undefined data in People
2026-03-14 17:01:06 -04:00
metacryst
cc8b5035fe Fixed screen size, added dark mode app menu icons, added haptics, added keyboard dismiss with swipe 2026-02-16 06:41:05 -06:00
metacryst
83a640433a adding app icon and splash screen 2026-02-15 05:32:58 -06:00
metacryst
cee8ebecc5 built ios app working 2026-02-14 21:43:55 -06:00
metacryst
f02f181058 Introducing dark mode, deriving apps in bottom bar from db 2026-02-13 20:25:46 -06:00
metacryst
4432acfea5 [BUG] fix double click to open app 2026-02-09 02:56:00 -06:00
metacryst
9d0149de75 Merge branch 'main' of https://git.sun.museum/sam/ForumMobile 2026-02-09 02:02:53 -06:00
metacryst
e548dbb6c9 add people icon 2026-02-09 02:02:19 -06:00
770d3bb012 Forum changes
- Added 2 new svg icons
- Fixed visual styling bugs on Forum page involving overflowing containers
- Added current network name underneath "Forum" title
- Input in Forum.js now sends ws message to server with new post, which is then broadcasted through the ws
- Forum app now displays a list of posts for the current network showing the author, time, text, whether it's been edited, and whether it's yours
- ForumPanel.js .onClick() contains the ws calls for DELETE and PUT
- Fixed bug where Forum would not scroll to top upon a new post/onAppear
- Modified ws GET call in ForumPanel.js to reflect new format
- Added .onEvent() handlers inForumPanel.js for "new-post", "deleted-post", and "edited-post"
- Changed AppMenu People icon
-
2026-02-08 22:44:19 -05:00
metacryst
3bf23daa7a Merge branch 'main' of https://git.sun.museum/sam/ForumMobile 2026-02-06 06:41:56 -06:00
metacryst
6cbb4c8dad basic sidebar toggle 2026-02-06 06:41:54 -06:00
b20ce6da06 People app and navigation + fixes
Index.js
- Fixed issue where incorrect pathname was set while not part of an organization.
- onNavigate(), fetchAppData(), getDefaultNetworkName(), and getDefaultAppName() all modified to account for user not being part of an organization (default path set to /my/dashboard)
- onNavigate() modified to properly set defaults for currentNetwork and currentApp, and modified to fix issue where app would crash when attempting to access nonexistent 'this.currentNetwork.data'
- onNavigate() now correctly switches currentApp

Home.js
- Changed ZStack to a VStack
- Replaced Jobs switch case with People
- Adjusted styling for AppMenu() and VStack height values
- Changed .onNavigate() from function() to () => {} for correct this binding
- !!!(TEMP FIX)!!!: Added extra rerender() call in an .onClick() after .onNavigate()

AppMenu()
- Modified app icons to be highlighted when active
- Changed Forum's app icon to redirect from "/" to "/dashboard"
- Removed fixed styling to account for Home's new responsive VStack layout

People.js
- Added People.js

Messages.js
- Fixed missing 'global." before Socket.send
- Changed 'vh' height values to 'pct' to account for AppMenu() at bottom
- Fixed misnamed modifiers

Forum.js
- Adjusted styling
- Changed 'vh' height values to 'pct' to account for AppMenu() at bottom

styles.css
- Added missing custom color vars
2026-02-02 04:34:26 -05:00
a68f35faf5 WS connection crash fix
- constructor() in index.js would attempt to fetch profile with getProfile() before websocket connection was established
2026-02-01 23:07:52 -05:00
metacryst
6262ce53aa establishing ws connection, starting on messages section 2026-02-01 16:11:11 -06:00
metacryst
6d50337e3b getting things going 2026-01-29 08:20:27 -06:00
sam
c09b08a474 Merge pull request 'master' (#1) from master into main
Reviewed-on: #1
2026-01-25 06:29:01 -06:00
metacryst
7f3f266d52 init 2026-01-25 06:27:57 -06:00
metacryst
fd27ac9c69 sidebar 2026-01-13 18:06:32 -06:00
Eric Lewellen
5b0f69b7aa readme update 2026-01-13 17:55:11 -06:00
Eric Lewellen
2d71dd0245 Merge branch 'readme' into 'master'
adding readme

See merge request hyper-drive/blockcatcher-app!2
2026-01-13 23:48:36 +00:00
Eric Lewellen
7a3dabc9aa adding readme 2026-01-13 17:47:57 -06:00
Eric Lewellen
aba685fb72 Merge branch 'main' into 'master'
Main 2 master

See merge request hyper-drive/blockcatcher-app!1
2026-01-13 23:38:53 +00:00
Eric Lewellen
f80b0b1bf3 removing default readme 2026-01-13 17:38:36 -06:00
Eric Lewellen
a0adeecb13 Initial commit 2026-01-13 23:20:00 +00:00
41 changed files with 883 additions and 3829 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ node_modules/
.DS_Store
.sourcemaps
dist/
package-lock.json

View File

@@ -1,27 +0,0 @@
## Created with Capacitor Create App
This app was created using [`@capacitor/create-app`](https://github.com/ionic-team/create-capacitor-app),
and comes with a very minimal shell for building an app.
### Running this example
To run the provided example, you can use `npm start` command.
```bash
npm start
```
### Background Color
In src/manifest.json, "#31d53d" refers to the green color which is visible in the background in the web version. This is not visible in the built version.
### Running iOS
https://capacitorjs.com/docs/ios#adding-the-ios-platform
npm install @capacitor/ios
npx cap add ios
npx cap open ios
To Rerun:
npm run build && npx cap copy ios

View File

@@ -1,7 +1,14 @@
{
"appId": "so.blockcatcher.app",
"appName": "Blockcatcher",
"appId": "so.forum.app",
"appName": "Forum",
"webDir": "dist",
"ios": {
"allowsBackForwardNavigationGestures": true
},
"server": {
"url": "http://sam.local:5173",
"cleartext": true
},
"plugins": {
"SplashScreen": {
"launchAutoHide": false

158
doc.md Normal file
View File

@@ -0,0 +1,158 @@
Quill is a SwiftUI-style JavaScript framework. It makes use of components called Shadows, which are HTML Custom Elements.
### Getting Started:
Take index.js and put it in your app. Typically as quill.js. Then import it in the head of the HTML.
### Basic Overview:
Quill uses components called Shadows. Each Shadow is a Custom HTML Element (https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)
```
class Home extends Shadow {
render() {
}
}
register(Home)
```
Once created, it can be imported like
```
import "Home.js"
```
(Not how we are NOT importing the actual class object. If that happens, it will fail.)
Here is an example of Hello World:
```
class Home extends Shadow {
render() {
p("Hello World")
.x(50, vw)
.y(50, vh)
}
}
register(Home)
```
This will render a paragraph tag in the middle of the screen.
Here's what it will look like in HTML:
```
<body>
<home->
<p style="position: absolute; top: 50vh; left: 50vw;">Hello World</p>
</home->
</body>
```
Note: .x() and .y() are quill-specific functions that were created simply for nice syntax. However, this would also be valid:
```
p("Hello World")
.top(50, vh)
.left(50, vw)
```
There are quill functions for every HTML style attribute. If they have units, they will follow the pattern directly above, where the first parameter is the amount and the second parameter is the unit.
### Real Basic Example:
First, you need your index.html. Here is one:
```
<!DOCTYPE html>
<html lang="en" class="public">
<head>
<title>Parchment</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="/_/icons/quill.svg">
<link rel="stylesheet" href="/_/code/shared.css">
<script src="/_/code/quill.js"></script>
<script type="module" src="75820185/index.js"></script>
</head>
<body>
</body>
</html>
```
When starting, it is typical to make a "Home" shadow and import it in index.js. Here is an example:
index.js:
```
import "./Home.js"
Home()
```
Home.js:
```
import "../components/NavBar.js"
import "./HomeContent.js"
import "./Why.js"
import "./Events.js"
import "./Join.js"
import "./SignIn.js"
import "./Success.js"
class Home extends Shadow {
render() {
ZStack(() => {
NavBar()
img("/_/icons/logo.svg", "2.5em")
.onClick((done) => {
if(!done) return
window.navigateTo("/")
})
.position("absolute")
.left(50, vw).top(4, em)
.center()
.transform(`translate(${window.isMobile() ? "-50%" : "-2em"}, -50%)`)
switch(window.location.pathname) {
case "/":
HomeContent()
break;
case "/why":
Why()
break;
case "/events":
Events()
break;
case "/join":
Join()
break;
case "/success":
Success()
break;
}
})
.onNavigate(() => {
this.rerender()
})
}
}
register(Home)
```
Success.js:
```
class Success extends Shadow {
render() {
p("Thanks for your purchase! You will receive a confirmation email shortly. <br><br> <b>Keep that email; it will be checked at the door.</b>")
.x(50, vw).y(50, vh)
.center()
}
}
register(Success)
```

View File

@@ -14,10 +14,37 @@
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
5ADE813A2FAD0ACB008DDDE2 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ADE81392FAD0ACB008DDDE2 /* NotificationService.swift */; };
5ADE813E2FAD0ACB008DDDE2 /* NotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 5ADE81372FAD0ACA008DDDE2 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
5ADE813C2FAD0ACB008DDDE2 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 504EC2FC1FED79650016851F /* Project object */;
proxyType = 1;
remoteGlobalIDString = 5ADE81362FAD0ACA008DDDE2;
remoteInfo = NotificationService;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
5ADE813F2FAD0ACB008DDDE2 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
5ADE813E2FAD0ACB008DDDE2 /* NotificationService.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
22352DD22F74F93C0052EF07 /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -27,6 +54,9 @@
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
5ADE81372FAD0ACA008DDDE2 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
5ADE81392FAD0ACB008DDDE2 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
5ADE813B2FAD0ACB008DDDE2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
@@ -41,6 +71,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5ADE81342FAD0ACA008DDDE2 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -56,6 +93,7 @@
isa = PBXGroup;
children = (
504EC3061FED79650016851F /* App */,
5ADE81382FAD0ACB008DDDE2 /* NotificationService */,
504EC3051FED79650016851F /* Products */,
7F8756D8B27F46E3366F6CEA /* Pods */,
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
@@ -66,6 +104,7 @@
isa = PBXGroup;
children = (
504EC3041FED79650016851F /* App.app */,
5ADE81372FAD0ACA008DDDE2 /* NotificationService.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -73,6 +112,7 @@
504EC3061FED79650016851F /* App */ = {
isa = PBXGroup;
children = (
22352DD22F74F93C0052EF07 /* App.entitlements */,
50379B222058CBB4000EE86E /* capacitor.config.json */,
504EC3071FED79650016851F /* AppDelegate.swift */,
504EC30B1FED79650016851F /* Main.storyboard */,
@@ -85,6 +125,15 @@
path = App;
sourceTree = "<group>";
};
5ADE81382FAD0ACB008DDDE2 /* NotificationService */ = {
isa = PBXGroup;
children = (
5ADE81392FAD0ACB008DDDE2 /* NotificationService.swift */,
5ADE813B2FAD0ACB008DDDE2 /* Info.plist */,
);
path = NotificationService;
sourceTree = "<group>";
};
7F8756D8B27F46E3366F6CEA /* Pods */ = {
isa = PBXGroup;
children = (
@@ -107,23 +156,42 @@
504EC3021FED79650016851F /* Resources */,
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,
E3424294FACA667401D6F46C /* [CP] Copy Pods Resources */,
5ADE813F2FAD0ACB008DDDE2 /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
5ADE813D2FAD0ACB008DDDE2 /* PBXTargetDependency */,
);
name = App;
productName = App;
productReference = 504EC3041FED79650016851F /* App.app */;
productType = "com.apple.product-type.application";
};
5ADE81362FAD0ACA008DDDE2 /* NotificationService */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5ADE81422FAD0ACB008DDDE2 /* Build configuration list for PBXNativeTarget "NotificationService" */;
buildPhases = (
5ADE81332FAD0ACA008DDDE2 /* Sources */,
5ADE81342FAD0ACA008DDDE2 /* Frameworks */,
5ADE81352FAD0ACA008DDDE2 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = NotificationService;
productName = NotificationService;
productReference = 5ADE81372FAD0ACA008DDDE2 /* NotificationService.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
504EC2FC1FED79650016851F /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastSwiftUpdateCheck = 1500;
LastUpgradeCheck = 0920;
TargetAttributes = {
504EC3031FED79650016851F = {
@@ -131,6 +199,10 @@
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
};
5ADE81362FAD0ACA008DDDE2 = {
CreatedOnToolsVersion = 15.0;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */;
@@ -147,6 +219,7 @@
projectRoot = "";
targets = (
504EC3031FED79650016851F /* App */,
5ADE81362FAD0ACA008DDDE2 /* NotificationService */,
);
};
/* End PBXProject section */
@@ -165,6 +238,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5ADE81352FAD0ACA008DDDE2 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -227,8 +307,24 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5ADE81332FAD0ACA008DDDE2 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5ADE813A2FAD0ACB008DDDE2 /* NotificationService.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
5ADE813D2FAD0ACB008DDDE2 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5ADE81362FAD0ACA008DDDE2 /* NotificationService */;
targetProxy = 5ADE813C2FAD0ACB008DDDE2 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
504EC30B1FED79650016851F /* Main.storyboard */ = {
isa = PBXVariantGroup;
@@ -360,16 +456,18 @@
isa = XCBuildConfiguration;
baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 53DK57C7ZF;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.6;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = so.hyperia.app;
PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forum;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
@@ -381,15 +479,17 @@
isa = XCBuildConfiguration;
baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 53DK57C7ZF;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = so.hyperia.app;
MARKETING_VERSION = 1.0.6;
PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forum;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_VERSION = 5.0;
@@ -397,6 +497,72 @@
};
name = Release;
};
5ADE81402FAD0ACB008DDDE2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 53DK57C7ZF;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = NotificationService/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = NotificationService;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forum.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
5ADE81412FAD0ACB008DDDE2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 53DK57C7ZF;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = NotificationService/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = NotificationService;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forum.NotificationService;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -418,6 +584,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5ADE81422FAD0ACB008DDDE2 /* Build configuration list for PBXNativeTarget "NotificationService" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5ADE81402FAD0ACB008DDDE2 /* Debug */,
5ADE81412FAD0ACB008DDDE2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 504EC2FC1FED79650016851F /* Project object */;

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1640"
version = "1.8">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:App.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:App.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:App.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>

View File

@@ -7,10 +7,38 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.enableInteractiveKeyboard()
}
// Override point for customization after application launch.
return true
}
func enableInteractiveKeyboard() {
guard let window = UIApplication.shared.windows.first,
let rootView = window.rootViewController?.view else {
return
}
// Find WKWebView recursively
func findWebView(in view: UIView) -> WKWebView? {
if let webView = view as? WKWebView {
return webView
}
for subview in view.subviews {
if let found = findWebView(in: subview) {
return found
}
}
return nil
}
if let webView = findWebView(in: rootView) {
webView.scrollView.keyboardDismissMode = .interactive
print("✅ Interactive keyboard enabled!")
}
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
@@ -46,4 +74,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: deviceToken)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "AppIcon-512@2x.png",
"filename" : "Group 73 (6).png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -1,23 +1,23 @@
{
"images" : [
{
"filename" : "Group 74.png",
"idiom" : "universal",
"filename" : "splash-2732x2732-2.png",
"scale" : "1x"
},
{
"filename" : "Group 75.png",
"idiom" : "universal",
"filename" : "splash-2732x2732-1.png",
"scale" : "2x"
},
{
"filename" : "Group 76.png",
"idiom" : "universal",
"filename" : "splash-2732x2732.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Hyperia</string>
<string>Forum</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@@ -22,6 +22,23 @@
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Used to find keep local information relevant.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Used to find forums and communities near you</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Access your photos to set a profile picture and share with others</string>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
@@ -30,18 +47,15 @@
<array>
<string>armv7</string>
</array>
<key>UIRequiresFullScreen</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,49 @@
//
// NotificationService.swift
// NotificationService
//
// Created by Matias Carulli on 5/7/26.
//
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
guard
let content = bestAttemptContent,
let imageUrlString = request.content.userInfo["imageUrl"] as? String,
let imageUrl = URL(string: imageUrlString)
else {
contentHandler(request.content)
return
}
URLSession.shared.downloadTask(with: imageUrl) { location, _, error in
defer { contentHandler(content) }
guard let location = location, error == nil else { return }
let tmpFile = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(imageUrl.lastPathComponent)
try? FileManager.default.moveItem(at: location, to: tmpFile)
if let attachment = try? UNNotificationAttachment(identifier: "avatar", url: tmpFile) {
content.attachments = [attachment]
}
}.resume()
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler, let content = bestAttemptContent {
contentHandler(content)
}
}
}

View File

@@ -12,9 +12,14 @@ def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera'
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
pod 'CapacitorGeolocation', :path => '../../node_modules/@capacitor/geolocation'
pod 'CapacitorGoogleMaps', :path => '../../node_modules/@capacitor/google-maps'
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
pod 'CapacitorPreferences', :path => '../../node_modules/@capacitor/preferences'
pod 'CapacitorPushNotifications', :path => '../../node_modules/@capacitor/push-notifications'
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
pod 'NotnotsamuelCapacitorSwipeBack', :path => '../../node_modules/@notnotsamuel/capacitor-swipe-back'
end
target 'App' do

View File

@@ -4,6 +4,9 @@ PODS:
- CapacitorCamera (7.0.2):
- Capacitor
- CapacitorCordova (7.4.4)
- CapacitorFilesystem (7.1.8):
- Capacitor
- IONFilesystemLib (~> 1.1.1)
- CapacitorGeolocation (7.1.5):
- Capacitor
- IONGeolocationLib (= 1.0.1)
@@ -11,6 +14,12 @@ PODS:
- Capacitor
- Google-Maps-iOS-Utils (~> 5.0)
- GoogleMaps (~> 8.4)
- CapacitorHaptics (7.0.3):
- Capacitor
- CapacitorPreferences (7.0.4):
- Capacitor
- CapacitorPushNotifications (7.0.6):
- Capacitor
- CapacitorSplashScreen (7.0.3):
- Capacitor
- Google-Maps-iOS-Utils (5.0.0):
@@ -20,20 +29,29 @@ PODS:
- GoogleMaps/Base (8.4.0)
- GoogleMaps/Maps (8.4.0):
- GoogleMaps/Base
- IONFilesystemLib (1.1.2)
- IONGeolocationLib (1.0.1)
- NotnotsamuelCapacitorSwipeBack (3.0.2):
- Capacitor
DEPENDENCIES:
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
- "CapacitorCamera (from `../../node_modules/@capacitor/camera`)"
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
- "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
- "CapacitorGeolocation (from `../../node_modules/@capacitor/geolocation`)"
- "CapacitorGoogleMaps (from `../../node_modules/@capacitor/google-maps`)"
- "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
- "CapacitorPreferences (from `../../node_modules/@capacitor/preferences`)"
- "CapacitorPushNotifications (from `../../node_modules/@capacitor/push-notifications`)"
- "CapacitorSplashScreen (from `../../node_modules/@capacitor/splash-screen`)"
- "NotnotsamuelCapacitorSwipeBack (from `../../node_modules/@notnotsamuel/capacitor-swipe-back`)"
SPEC REPOS:
trunk:
- Google-Maps-iOS-Utils
- GoogleMaps
- IONFilesystemLib
- IONGeolocationLib
EXTERNAL SOURCES:
@@ -43,24 +61,40 @@ EXTERNAL SOURCES:
:path: "../../node_modules/@capacitor/camera"
CapacitorCordova:
:path: "../../node_modules/@capacitor/ios"
CapacitorFilesystem:
:path: "../../node_modules/@capacitor/filesystem"
CapacitorGeolocation:
:path: "../../node_modules/@capacitor/geolocation"
CapacitorGoogleMaps:
:path: "../../node_modules/@capacitor/google-maps"
CapacitorHaptics:
:path: "../../node_modules/@capacitor/haptics"
CapacitorPreferences:
:path: "../../node_modules/@capacitor/preferences"
CapacitorPushNotifications:
:path: "../../node_modules/@capacitor/push-notifications"
CapacitorSplashScreen:
:path: "../../node_modules/@capacitor/splash-screen"
NotnotsamuelCapacitorSwipeBack:
:path: "../../node_modules/@notnotsamuel/capacitor-swipe-back"
SPEC CHECKSUMS:
Capacitor: 09d9ff8e9618e8c4b3cab2bbee34a17215dd2fef
CapacitorCamera: 6e18d54c8ab30d7dc7b8cd93d96f9b4f57e9202a
CapacitorCordova: bf648a636f3c153f652d312ae145fb508b6ffced
CapacitorFilesystem: f54cd6b76be06fa7ceb219cf313d32e0d626ea87
CapacitorGeolocation: b96474c3259dd4a294227ea8ec19140b1837cceb
CapacitorGoogleMaps: 20b5445a532f80dbb120fa99941fd094bcc88af6
CapacitorHaptics: d17da7dd984cae34111b3f097ccd3e21f9feec62
CapacitorPreferences: d82a7e3b95fcab43a553268b803356522910d153
CapacitorPushNotifications: c6158ba6f3777f281a675aa43e4011e9723e822b
CapacitorSplashScreen: d06ae8804808e9f649a08e7bb7f283c77b688084
Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321
GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d
IONFilesystemLib: 21a63377696b2d8fab5632ecfb7d2ac67bddb68a
IONGeolocationLib: 20f9d0248a0b5264511fb57a37e25dd2badf797a
NotnotsamuelCapacitorSwipeBack: 07a8985928db83b1d62a1d596246db466e69d149
PODFILE CHECKSUM: 1f8c41a3cb5e4540693adb6a47064e328eec261d
PODFILE CHECKSUM: d7fbdcfecbb81edc39c9975b338d5d15e186424b
COCOAPODS: 1.15.2

2275
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "capacitor-app",
"version": "1.0.0",
"name": "Forum",
"version": "1.0.1",
"description": "An Amazing Capacitor App",
"type": "module",
"keywords": [
@@ -13,12 +13,17 @@
"preview": "vite preview"
},
"dependencies": {
"@capacitor/camera": "latest",
"@capacitor/core": "latest",
"@capacitor/camera": "^7.0.2",
"@capacitor/core": "^7.4.4",
"@capacitor/filesystem": "^7.1.8",
"@capacitor/geolocation": "^7.1.5",
"@capacitor/google-maps": "^7.2.0",
"@capacitor/haptics": "^7.0.3",
"@capacitor/ios": "^7.4.4",
"@capacitor/splash-screen": "latest"
"@capacitor/preferences": "^7.0.4",
"@capacitor/push-notifications": "^7.0.6",
"@capacitor/splash-screen": "^7.0.3",
"@notnotsamuel/capacitor-swipe-back": "^3.0.2"
},
"devDependencies": {
"@capacitor/cli": "latest",

74
readme.md Normal file
View File

@@ -0,0 +1,74 @@
## Run in Browser
```npm run start```
## Install Native Tools
https://capacitorjs.com/docs/ios#adding-the-ios-platform
npm install @capacitor/ios
npx cap add ios
## Run On Device
To Open XCode:
npx cap open ios
Run this command to rebuild for iOS
npm run build && npx cap copy ios
If getting black screen:
npx cap sync iOS
## iOS:
### Browser: Dev Front and Dev Back (localhost)
This option should be at the top level of capacitor.config.json
"server": {
"url": "http://sam.local:5173",
"cleartext": true
},
### Browser: Prod Front and Prod Back
Run:
vite build
npx serve dist
If you need to login again:
run localStorage.clear() in the browser dev tools console and then refresh the page.
## iOS:
### Dev Front, Dev Back (localhost)
This option should be at the top level of capacitor.config.json
"server": {
"url": "http://sam.local:5173",
"cleartext": true
},
### Dev Front, Prod Back (frm.so)
Add "https://frm.so" to VITE_API_URL in .env.development
### Prod Front, Prod Back (frm.so)
Remove the "server" object from capacitor.config.
### Various Commands
npx cap config - this will list the full configuration currently being used
### Architecture
In Development, API routes are routed using the vite.config.js.
### Notes
Background Color:
In src/manifest.json, "#31d53d" refers to the green color which is visible in the background in the web version. This is not visible in the built version.
Test Push Notifications:
https://icloud.developer.apple.com/dashboard/notifications/teams/53DK57C7ZF/app/russell.sam.forum/notifications/create?notificationId=8bb87cf2-9590-4a63-b7e1-e4c7f2a2c879&environment=DEVELOPMENT&notificationType=push
Note: Even if built in "production" mode, the tokens will still be considered "development" by Apple until the app is actually deployed

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +0,0 @@
:root {
--main: #AEBDFF;
--accent: #60320c;
--text: #340000;
--yellow: #f1f3c3;
}
@media (prefers-color-scheme: dark) {
:root {
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100px" height="100px"><path d="M 17 21 A 1.0001 1.0001 0 0 0 16 22 L 16 32 A 1.0001 1.0001 0 0 0 17 33 L 83 33 A 1.0001 1.0001 0 0 0 84 32 L 84 22 A 1.0001 1.0001 0 0 0 83 21 L 17 21 z M 18 23 L 82 23 L 82 31 L 18 31 L 18 29 L 73.5 29 A 0.50005 0.50005 0 1 0 73.5 28 L 18 28 L 18 23 z M 76.5 28 A 0.50005 0.50005 0 1 0 76.5 29 L 78.5 29 A 0.50005 0.50005 0 1 0 78.5 28 L 76.5 28 z M 17 44 A 1.0001 1.0001 0 0 0 16 45 L 16 55 A 1.0001 1.0001 0 0 0 17 56 L 83 56 A 1.0001 1.0001 0 0 0 84 55 L 84 45 A 1.0001 1.0001 0 0 0 83 44 L 17 44 z M 18 46 L 82 46 L 82 54 L 18 54 L 18 46 z M 22.5 51 A 0.50005 0.50005 0 1 0 22.5 52 L 67.5 52 A 0.50005 0.50005 0 1 0 67.5 51 L 22.5 51 z M 70.5 51 A 0.50005 0.50005 0 1 0 70.5 52 L 73.5 52 A 0.50005 0.50005 0 1 0 73.5 51 L 70.5 51 z M 76.5 51 A 0.50005 0.50005 0 1 0 76.5 52 L 78.5 52 A 0.50005 0.50005 0 1 0 78.5 51 L 76.5 51 z M 17 67 A 1.0001 1.0001 0 0 0 16 68 L 16 78 A 1.0001 1.0001 0 0 0 17 79 L 83 79 A 1.0001 1.0001 0 0 0 84 78 L 84 68 A 1.0001 1.0001 0 0 0 83 67 L 17 67 z M 18 69 L 82 69 L 82 74 L 26.5 74 A 0.50005 0.50005 0 1 0 26.5 75 L 82 75 L 82 77 L 18 77 L 18 69 z M 21.5 74 A 0.50005 0.50005 0 1 0 21.5 75 L 23.5 75 A 0.50005 0.50005 0 1 0 23.5 74 L 21.5 74 z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

View File

@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 511.697 511.697" xml:space="preserve">
<g transform="translate(0 1)">
<g>
<path style="fill:#FFE100;" d="M505.189,119.472L505.189,119.472c-7.81-7.81-13.885-9.546-21.695-1.736l-45.125,45.993
c-6.075,6.075-14.753,6.075-20.827,0.868l-63.349-59.01c-4.339-4.339-10.414-6.942-16.488-6.942h-94.59l-79.837,75.498
c-7.81,7.81-9.546,21.695-1.736,30.373c8.678,9.546,22.563,8.678,31.241,0l59.01-62.481h43.39L147.656,306.916H62.612
c-13.885,0-26.902,10.414-27.769,24.298c-0.868,15.62,11.281,27.77,26.034,27.77H165.88c10.414,0,19.959-4.339,27.77-12.149
l57.275-61.614l61.614,65.085L296.05,456.177c-2.603,12.149,2.603,25.166,13.885,30.373c15.62,6.942,32.108-1.736,36.447-17.356
l25.166-135.376c0.868-6.075-0.868-11.281-5.207-15.62l-71.159-72.027l69.424-69.424l45.993,45.993
c7.81,7.81,21.695,7.81,29.505,0l19.959-19.959l45.125-52.068C513.867,142.035,513.867,128.15,505.189,119.472"/>
<path style="fill:#FFE100;" d="M462.667,118.605l-0.868-0.868c-7.81-7.81-1.736-7.81-10.414,0l-34.712,45.993
c-6.075,6.075-14.753,6.075-20.827,0.868l-73.763-59.01c-4.339-4.339-4.339-6.942-10.414-6.942h-68.556l-79.837,75.498
c-7.81,7.81-9.546,21.695-1.736,30.373c8.678,9.546,4.339,8.678,12.149,0l60.746-62.481h34.712L121.623,306.916h-59.01
c-13.885,0-26.902,10.414-27.769,24.298c-0.868,15.62,11.281,27.77,26.034,27.77h95.458l69.424-72.895l60.746,64.217
l-16.488,105.871c-2.603,12.149,2.603,25.166,13.885,30.373c15.62,6.942,16.488-1.736,19.959-17.356l20.827-144.922
l-55.539-78.102l63.349-69.424l40.786,45.993c7.81,7.81,18.224-3.471,26.034-11.281l8.678-8.678l54.671-54.671
C470.477,140.299,470.477,126.415,462.667,118.605"/>
</g>
<g>
<path style="fill:#FFA800;" d="M323.819,324.272l-20.827,144.922c-2.603,11.281-3.471,19.092-10.414,19.959
c12.149,0.868,23.431-7.81,26.034-19.959l27.77-144.922l-77.234-77.234L323.819,324.272z"/>
<path style="fill:#FFA800;" d="M487.833,118.605l-0.868-0.868c-7.81-7.81-19.092-7.81-27.77-1.736
c0.868,0.868,0.868,0.868,1.736,1.736l0.868,0.868c7.81,7.81,7.81,20.827,0,29.505l-54.671,54.671l-8.678,8.678
c-4.339,4.339-8.678,9.546-13.885,12.149c7.81,6.942,20.827,6.942,28.637-0.868l19.959-19.959l54.671-54.671
C495.643,140.299,495.643,127.282,487.833,118.605"/>
</g>
<g>
<path style="fill:#FFFFFF;" d="M163.277,207.12c-0.868-0.868-1.736-1.736-2.603-2.603c-7.81-8.678-6.942-21.695,1.736-30.373
l80.705-75.498H217.08l-79.837,75.498c-7.81,7.81-9.546,21.695-1.736,30.373S155.467,213.194,163.277,207.12"/>
<path style="fill:#FFFFFF;" d="M34.843,331.215c0.868-13.885,13.017-24.298,26.902-24.298H35.711
c-13.885,0-26.902,10.414-27.769,24.298c0,15.62,12.149,27.77,26.902,27.77h26.034C46.124,358.984,33.975,346.835,34.843,331.215"
/>
</g>
<path style="fill:#FFE100;" d="M425.351,63.933c0-24.298-19.092-43.39-43.39-43.39c-0.868,0-26.034,19.092-26.034,43.39
c0,0.868,1.736,43.39,26.034,43.39C406.26,107.323,425.351,88.232,425.351,63.933"/>
<g>
<path style="fill:#FFA800;" d="M399.318,20.543c-2.603,0-6.075,0-8.678,0.868c19.959,4.339,34.712,21.695,34.712,42.522
s-14.753,38.183-34.712,42.522c2.603,0.868,6.075,0.868,8.678,0.868c24.298,0,43.39-19.092,43.39-43.39
S423.616,20.543,399.318,20.543"/>
<path style="fill:#FFA800;" d="M356.795,71.743c0,0.868,0,0.868,0,1.736C356.795,72.611,356.795,71.743,356.795,71.743"/>
</g>
<path d="M295.182,497.832c-5.207,0-9.546-0.868-14.753-3.471c-14.753-6.942-23.431-23.431-19.092-40.786l15.62-100.664
l-52.068-54.671l-62.481,66.82c-1.736,1.736-3.471,2.603-6.075,2.603H34.843c-9.546,0-18.224-4.339-25.166-10.414
c-6.942-6.942-10.414-16.488-9.546-26.034c0.868-18.224,16.488-32.976,36.447-32.976h81.573l32.108-35.58
c3.471-3.471,8.678-3.471,12.149-0.868c3.471,3.471,3.471,8.678,0.868,12.149l-34.712,39.051c-2.603,1.736-4.339,2.603-6.942,2.603
H36.579c-10.414,0-19.092,6.942-19.092,16.488c0,5.207,1.736,9.546,4.339,13.017c3.471,3.471,7.81,5.207,13.017,5.207h118.02
l65.953-70.292c1.736-1.736,4.339-2.603,6.075-2.603c2.603,0,4.339,0.868,6.075,2.603l61.614,64.217
c1.736,1.736,2.603,4.339,2.603,6.942l-16.488,105.871c-1.736,9.546,1.736,18.224,9.546,21.695c5.207,2.603,10.414,1.736,14.753,0
c4.339-2.603,7.81-6.942,9.546-11.281l26.034-139.715l-74.63-74.631c-2.603-2.603-3.471-4.339-3.471-6.942
c0-2.603,0.868-4.339,2.603-6.075l69.424-69.424c3.471-3.471,8.678-3.471,12.149,0l45.993,45.993
c4.339,4.339,12.149,4.339,17.356,0l74.63-74.631c4.339-4.339,4.339-12.149,0-16.488l-0.868-0.868
c-5.207-5.207-13.017-5.207-17.356,0l-45.125,45.993c-8.678,8.678-23.431,9.546-32.976,0.868l-63.349-59.01
c-2.603-2.603-6.942-4.339-10.414-4.339h-91.986l-77.234,73.763c-5.207,5.207-5.207,13.017-0.868,18.224
c2.603,2.603,6.075,4.339,9.546,4.339l0,0c3.471,0,6.942-1.736,8.678-4.339l59.01-62.481c1.736-1.736,4.339-2.603,6.075-2.603
h43.39c3.471,0,6.942,1.736,7.81,5.207s0.868,6.942-1.736,9.546l-69.424,78.102c-3.471,3.471-8.678,4.339-12.149,0.868
c-3.471-3.471-3.471-8.678-0.868-12.149l56.407-63.349h-19.959l-56.407,59.878c-5.207,6.075-13.017,9.546-21.695,9.546l0,0
c-8.678,0-16.488-3.471-21.695-9.546c-10.414-11.281-9.546-30.373,1.736-42.522l79.837-76.366c1.736-3.471,3.471-4.339,6.075-4.339
h94.59c8.678,0,16.488,3.471,22.563,8.678l63.349,59.01c2.603,2.603,6.075,2.603,8.678,0l45.125-45.993
c11.281-11.281,30.373-11.281,42.522,0l0.868,0.868c11.281,11.281,11.281,30.373,0,41.654l-74.63,74.631
c-11.281,11.281-30.373,11.281-41.654,0l-39.919-39.919l-57.275,57.275l72.027,72.027c1.736,1.736,2.603,5.207,2.603,7.81
L329.026,470.93c-2.603,10.414-9.546,19.092-18.224,23.431C305.595,496.964,300.389,497.832,295.182,497.832z"/>
<path d="M187.575,241.832c0-5.207-3.471-8.678-8.678-8.678s-8.678,3.471-8.678,8.678s3.471,8.678,8.678,8.678
S187.575,247.038,187.575,241.832"/>
<path d="M399.318,116.001c-28.637,0-52.068-23.431-52.068-52.068s23.431-52.068,52.068-52.068
c28.637,0,52.068,23.431,52.068,52.068S427.955,116.001,399.318,116.001z M399.318,29.221c-19.092,0-34.712,15.62-34.712,34.712
s15.62,34.712,34.712,34.712c19.092,0,34.712-15.62,34.712-34.712S418.409,29.221,399.318,29.221z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,12 +0,0 @@
html,
body {
padding: 0;
margin: 0;
}
body {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}

View File

@@ -2,28 +2,130 @@
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Blockcatcher</title>
<title>Forum</title>
<meta
name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
content="viewport-fit=auto, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<script
type="module"
src="https://unpkg.com/@ionic/pwa-elements@latest/dist/ionicpwaelements/ionicpwaelements.esm.js"
></script>
<script
nomodule
src="https://unpkg.com/@ionic/pwa-elements@latest/dist/ionicpwaelements/ionicpwaelements.js"
></script>
<link rel="icon" type="image/x-icon" href="./_/icons/runner.svg" />
<link rel="manifest" href="./manifest.json" />
<link rel="stylesheet" href="./_/code/styles.css" />
<script src="./_/code/quill.js"></script>
<script type="module" src="./index.js"></script>
<link rel="stylesheet" href="" />
<script>window.config = { UI: 'https://frm.so', SERVER: 'https://frm.so' }</script>
<script type="module">
await import('./mobileutil.js')
function appendScript(src, isModule = false) {
return new Promise((resolve, reject) => {
const s = document.createElement('script')
s.src = src
s.crossOrigin = "anonymous"
if (isModule) s.type = 'module'
s.onload = resolve
s.onerror = reject
document.head.appendChild(s)
})
}
function appendStylesheet(href, { replaceExisting = false } = {}) {
return new Promise((resolve, reject) => {
if (replaceExisting) {
document.querySelector('link[rel="stylesheet"]')?.remove();
}
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
link.onload = resolve;
link.onerror = reject;
document.head.appendChild(link);
});
}
try {
await appendStylesheet(`${window.config.UI}/_/code/shared.css`, { replaceExisting: true });
await appendScript(window.config.UI + '/_/code/quill.js')
await appendScript(window.config.UI + '/83947261/index.js', true)
} catch (e) {
document.body.innerHTML = `
<style>
#ptr-screen {
position: fixed; inset: 0;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
font-family: sans-serif; text-align: center;
padding: 2rem; touch-action: none;
transition: transform 0.2s ease;
color: var(--text);
}
#ptr-icon {
font-size: 2rem; margin-bottom: 1rem;
transition: transform 0.2s ease, opacity 0.2s ease;
opacity: 0.4;
}
#ptr-label {
font-size: 0.9rem; opacity: 0.5;
margin-top: 0.5rem;
}
#ptr-indicator {
color: var(--text);
position: fixed; top: 5vh; left: 0; right: 0;
display: flex; align-items: center; justify-content: center;
padding-top: env(safe-area-inset-top);
height: 0; overflow: hidden;
transition: height 0.1s ease;
font-size: 0.8rem; opacity: 0.6; gap: 0.4rem;
}
@keyframes spin { to { transform: rotate(360deg) } }
.spinning { animation: spin 0.6s linear infinite }
</style>
<div id="ptr-indicator">
<span id="ptr-arrow">↓</span>
<span id="ptr-hint">Pull to retry</span>
</div>
<div id="ptr-screen">
<div id="ptr-icon">⚠️</div>
<p style="margin:0;font-size:1.1rem;font-weight:600">No connection</p>
<p id="ptr-label">Could not reach the server.<br>Pull down to try again.</p>
</div>
`
const THRESHOLD = 90
let startY = 0, dragging = false
document.addEventListener('touchstart', e => {
startY = e.touches[0].clientY
dragging = true
})
document.addEventListener('touchmove', e => {
if (!dragging) return
const dy = Math.max(0, e.touches[0].clientY - startY)
const pull = Math.min(dy, THRESHOLD * 1.5)
const progress = Math.min(pull / THRESHOLD, 1)
document.getElementById('ptr-screen').style.transform = `translateY(${pull * 0.4}px)`
document.getElementById('ptr-indicator').style.height = (pull * 0.6) + 'px'
document.getElementById('ptr-arrow').style.transform = `rotate(${progress * 180}deg)`
document.getElementById('ptr-hint').textContent = progress >= 1 ? 'Release to retry' : 'Pull to retry'
document.getElementById('ptr-icon').style.opacity = 0.4 + progress * 0.6
})
document.addEventListener('touchend', e => {
if (!dragging) return
dragging = false
const dy = e.changedTouches[0].clientY - startY
if (dy >= THRESHOLD) {
document.getElementById('ptr-arrow').textContent = '↻'
document.getElementById('ptr-arrow').classList.add('spinning')
document.getElementById('ptr-hint').textContent = 'Retrying…'
setTimeout(() => location.reload(), 400)
} else {
document.getElementById('ptr-screen').style.transform = ''
document.getElementById('ptr-indicator').style.height = '0'
}
})
}
</script>
<meta name="theme-color" content="#31d53d" />
</head>
<body>

View File

@@ -1,3 +0,0 @@
import "./js/Home.js"
Home()
document.body.style.backgroundColor = "var(--main)"

View File

@@ -1,197 +0,0 @@
class Home extends Shadow {
tracking = false
logs = []
timer = null
async startTracking() {
if (!navigator.geolocation) {
alert("Geolocation is not supported by your browser.");
return;
}
this.tracking = true
navigator.geolocation.requestPermission?.() || Promise.resolve('granted');
const permission = await new Promise((resolve) => {
navigator.geolocation.getCurrentPosition(
() => resolve('granted'),
() => resolve('denied')
);
});
if (permission === 'denied') {
alert("Location permission required");
this.tracking = false
return;
}
const timer = setInterval(async () => {
navigator.geolocation.getCurrentPosition(
async (pos) => {
const { latitude, longitude } = pos.coords;
const now = new Date();
const log = `${now.toISOString()} — (${latitude}, ${longitude})`;
const timestamp = this.formatCentralTime(now);
this.logs = [log, ...this.logs]
this.updateLogs()
await this.sendLocation(latitude, longitude, timestamp);
},
(err) => console.error("Location error:", err),
{ enableHighAccuracy: true }
);
}, 1000);
this.timer = timer
}
stopTracking() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
this.tracking = false;
}
}
render() {
ZStack(() => {
// Title bar
HStack(() => {
img("./_/icons/runner.svg", "2em", "2em")
p("San Marcos, TX")
.fontSize(1.2, em)
img("./_/icons/hamburger.svg", "2em", "2em")
})
.gap(15, vw)
.position("fixed")
.top(0)
.left(0)
.width(100, vw)
.paddingTop(2, em)
.paddingBottom(2, em)
.borderBottom("0.1rem solid var(--text)")
.backgroundColor("var(--yellow)")
.fontWeight("bold")
.display("flex")
.alignItems("center")
.justifyContent("center")
.zIndex(10)
VStack(() => {
button(() => {
if(this.tracking) {
Rectangle("48%", "48%")
.fill("#264B61")
.stroke("0.2em", "black")
} else {
Triangle("58%", "58%")
.fill("#9F292B")
.stroke("0.2em", "black")
}
})
.attr({"id": "playButton"})
.width(120, px)
.height(120, px)
.x(50, vw).y(50, vh)
.center()
.borderRadius(50, "%")
.backgroundColor(this.tracking ? "#CD593E" : "#A6EABD")
.border("0.2em solid black")
.cursor("pointer")
.display("flex")
.alignItems("center")
.justifyContent("center")
.onTap(() => {
console.log("tapped")
if (this.tracking) {
this.stopTracking();
this.rerender()
} else {
this.startTracking();
this.rerender()
}
})
VStack(() => {
this.logs.map(log =>
div(log)
.paddingHorizontal("8px")
.paddingVertical("12px")
.backgroundColor("rgba(255,255,255,0.9)")
.borderRadius(6, px)
.marginBottom(6, px)
.fontSize(0.9, rem)
.color("#222")
.fontFamily("monospace")
)
})
.attr({"id": "logList"})
.flex(1)
.overflowY("auto")
.paddingHorizontal(16, px)
})
.marginTop(7, em)
})
.overflowX("hidden")
.width(100, vw)
.height(100, vh)
.display("block")
.margin(0)
.color("var(--text)")
.fontFamily("Arial")
}
showTracking() {
this.$("#playButton").rerender()
this.$("#playButton").style.backgroundColor = this.tracking ? "#CD593E" : "#A6EABD"
}
updateLogs() {
let list = this.$("#logList")
list.rerender()
list.attr({"id": "logList"})
}
formatCentralTime(date) {
return new Intl.DateTimeFormat('en-US', {
timeZone: 'America/Chicago',
month: '2-digit',
day: '2-digit',
year: '2-digit',
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
hour12: true
})
.format(date)
.replace(/,/, '') // "04/05/25, 2:30:00 PM" → "04/05/25 2:30:00 PM"
.replace(/\//g, '.') // → "04.05.25 2:30:00 PM"
.toLowerCase()
.replace(' pm', 'pm')
.replace(' am', 'am');
}
async sendLocation(lat, lon, timestamp) {
try {
const resp = await fetch('http://localhost:3008/api/location', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: "Freddy Krueger",
latitude: lat,
longitude: lon,
timestamp
})
});
if (!resp.ok) throw new Error(resp.status);
console.log('Location sent');
} catch (e) {
console.error('Failed to send location:', e);
}
}
}
register(Home)

View File

@@ -4,9 +4,9 @@
"start_url": "index.html",
"display": "standalone",
"icons": [{
"src": "_/imgs/logo.png",
"src": "_/icons/logo.svg",
"sizes": "512x512",
"type": "image/png"
"type": "image/svg"
}],
"background_color": "#31d53d",
"theme_color": "#31d53d"

43
src/mobileutil.js Normal file
View File

@@ -0,0 +1,43 @@
import { PushNotifications } from '@capacitor/push-notifications';
import { Preferences } from '@capacitor/preferences';
import { Filesystem, Directory } from '@capacitor/filesystem';
import { Haptics, ImpactStyle } from '@capacitor/haptics';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';
import { Geolocation } from '@capacitor/geolocation';
import { SplashScreen } from '@capacitor/splash-screen';
import { CapacitorSwipeBackPlugin } from '@notnotsamuel/capacitor-swipe-back';
window.capacitor = {
Preferences,
PushNotifications,
Filesystem,
Directory,
Haptics,
ImpactStyle,
Camera,
CameraResultType,
CameraSource,
Geolocation,
SplashScreen,
CapacitorSwipeBackPlugin
}
window.mobileUtil = class mobileUtil {
static async authFetch(url, options = {}) {
const { value: token } = await Preferences.get({ key: 'auth_token' });
return fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`,
'X-Client': 'mobile'
}
});
}
static async removeAuthToken() {
await Preferences.remove({ key: 'auth_token'})
}
}

View File

@@ -6,9 +6,45 @@ export default defineConfig({
outDir: '../dist',
minify: false,
emptyOutDir: true,
sourcemap: true,
target: 'esnext' // modern version of browsers, allows top-level await
},
server: {
proxy: {
"/ws": {
target: "http://localhost:10002",
changeOrigin: true,
ws: true
},
"/profile/upload-image": {
target: "http://localhost:10002",
changeOrigin: true
},
"/api": {
target: "http://localhost:10002",
changeOrigin: true
},
"/db": {
target: "http://localhost:10002",
changeOrigin: true
},
"/apps": {
target: "http://localhost:10002",
changeOrigin: true
},
"/auth": {
target: "http://localhost:10002",
changeOrigin: true
},
"/@server": {
target: "http://localhost:10002",
changeOrigin: true
}
},
host: true,
allowedHosts: ['sam.local'],
},
esbuild: {
keepNames: true
}
});