Add native app shortcuts for common features (#604)
This commit is contained in:
parent
31d9c6bd9c
commit
b43309f0c3
11 changed files with 279 additions and 3 deletions
|
|
@ -38,6 +38,10 @@
|
|||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
|
|
|
|||
27
android/app/src/main/res/drawable/shortcut_inbox.xml
Normal file
27
android/app/src/main/res/drawable/shortcut_inbox.xml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M12,0A12,12 0,1 1,12 24A12,12 0,1 1,12 0Z" />
|
||||
<group
|
||||
android:scaleX="0.6666667"
|
||||
android:scaleY="0.6666667"
|
||||
android:translateX="4"
|
||||
android:translateY="4">
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:pathData="M2 12.0002C2 7.2862 2 4.92918 3.46447 3.46471C4.92893 2.00024 7.28595 2.00024 12 2.00024C16.714 2.00024 19.0711 2.00024 20.5355 3.46471C22 4.92918 22 7.2862 22 12.0002C22 16.7143 22 19.0713 20.5355 20.5358C19.0711 22.0002 16.714 22.0002 12 22.0002C7.28595 22.0002 4.92893 22.0002 3.46447 20.5358C2 19.0713 2 16.7143 2 12.0002Z"
|
||||
android:strokeColor="#FF202124"
|
||||
android:strokeWidth="1.5" />
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:pathData="M2 13.0002H5.16026C6.06543 13.0002 6.51802 13.0002 6.91584 13.1832C7.31367 13.3662 7.60821 13.7098 8.19729 14.3971L8.80271 15.1034C9.39179 15.7907 9.68633 16.1343 10.0842 16.3173C10.482 16.5002 10.9346 16.5002 11.8397 16.5002H12.1603C13.0654 16.5002 13.518 16.5002 13.9158 16.3173C14.3137 16.1343 14.6082 15.7907 15.1973 15.1034L15.8027 14.3971C16.3918 13.7098 16.6863 13.3662 17.0842 13.1832C17.482 13.0002 17.9346 13.0002 18.8397 13.0002H22"
|
||||
android:strokeColor="#FF202124"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeLineCap="round" />
|
||||
</group>
|
||||
</vector>
|
||||
27
android/app/src/main/res/drawable/shortcut_messages.xml
Normal file
27
android/app/src/main/res/drawable/shortcut_messages.xml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M12,0A12,12 0,1 1,12 24A12,12 0,1 1,12 0Z" />
|
||||
<group
|
||||
android:scaleX="0.6666667"
|
||||
android:scaleY="0.6666667"
|
||||
android:translateX="4"
|
||||
android:translateY="4">
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:pathData="M2 12.0002C2 8.22901 2 6.34339 3.17157 5.17182C4.34315 4.00024 6.22876 4.00024 10 4.00024H14C17.7712 4.00024 19.6569 4.00024 20.8284 5.17182C22 6.34339 22 8.22901 22 12.0002C22 15.7715 22 17.6571 20.8284 18.8287C19.6569 20.0002 17.7712 20.0002 14 20.0002H10C6.22876 20.0002 4.34315 20.0002 3.17157 18.8287C2 17.6571 2 15.7715 2 12.0002Z"
|
||||
android:strokeColor="#FF202124"
|
||||
android:strokeWidth="1.5" />
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:pathData="M6 8.00024L8.1589 9.79932C9.99553 11.3299 10.9139 12.0951 12 12.0951C13.0861 12.0951 14.0045 11.3299 15.8411 9.79932L18 8.00024"
|
||||
android:strokeColor="#FF202124"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeLineCap="round" />
|
||||
</group>
|
||||
</vector>
|
||||
27
android/app/src/main/res/drawable/shortcut_search.xml
Normal file
27
android/app/src/main/res/drawable/shortcut_search.xml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M12,0A12,12 0,1 1,12 24A12,12 0,1 1,12 0Z" />
|
||||
<group
|
||||
android:scaleX="0.6666667"
|
||||
android:scaleY="0.6666667"
|
||||
android:translateX="4"
|
||||
android:translateY="4">
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:pathData="M2,11.5005a9.5,9.5 0,1 1,19 0a9.5,9.5 0,1 1,-19 0"
|
||||
android:strokeColor="#FF202124"
|
||||
android:strokeWidth="1.5" />
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:pathData="M18.5 18.5005L22 22.0005"
|
||||
android:strokeColor="#FF202124"
|
||||
android:strokeWidth="1.5"
|
||||
android:strokeLineCap="round" />
|
||||
</group>
|
||||
</vector>
|
||||
31
android/app/src/main/res/drawable/shortcut_spaces.xml
Normal file
31
android/app/src/main/res/drawable/shortcut_spaces.xml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M12,0A12,12 0,1 1,12 24A12,12 0,1 1,12 0Z" />
|
||||
<group
|
||||
android:scaleX="0.6666667"
|
||||
android:scaleY="0.6666667"
|
||||
android:translateX="4"
|
||||
android:translateY="4">
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:pathData="M2.5 6.50049C2.5 4.61487 2.5 3.67206 3.08579 3.08627C3.67157 2.50049 4.61438 2.50049 6.5 2.50049C8.38562 2.50049 9.32843 2.50049 9.91421 3.08627C10.5 3.67206 10.5 4.61487 10.5 6.50049V17.5005C10.5 19.3861 10.5 20.3289 9.91421 20.9147C9.32843 21.5005 8.38562 21.5005 6.5 21.5005C4.61438 21.5005 3.67157 21.5005 3.08579 20.9147C2.5 20.3289 2.5 19.3861 2.5 17.5005V6.50049Z"
|
||||
android:strokeColor="#FF202124"
|
||||
android:strokeWidth="1.5" />
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:pathData="M13.5 15.5005C13.5 13.6149 13.5 12.6721 14.0858 12.0863C14.6716 11.5005 15.6144 11.5005 17.5 11.5005C19.3856 11.5005 20.3284 11.5005 20.9142 12.0863C21.5 12.6721 21.5 13.6149 21.5 15.5005V17.5005C21.5 19.3861 21.5 20.3289 20.9142 20.9147C20.3284 21.5005 19.3856 21.5005 17.5 21.5005C15.6144 21.5005 14.6716 21.5005 14.0858 20.9147C13.5 20.3289 13.5 19.3861 13.5 17.5005V15.5005Z"
|
||||
android:strokeColor="#FF202124"
|
||||
android:strokeWidth="1.5" />
|
||||
<path
|
||||
android:fillColor="@android:color/transparent"
|
||||
android:pathData="M13.5 5.50049C13.5 4.56861 13.5 4.10266 13.6522 3.73512C13.8552 3.24507 14.2446 2.85572 14.7346 2.65273C15.1022 2.50049 15.5681 2.50049 16.5 2.50049H18.5C19.4319 2.50049 19.8978 2.50049 20.2654 2.65273C20.7554 2.85572 21.1448 3.24507 21.3478 3.73512C21.5 4.10266 21.5 4.56861 21.5 5.50049C21.5 6.43237 21.5 6.89831 21.3478 7.26586C21.1448 7.75591 20.7554 8.14526 20.2654 8.34825C19.8978 8.50049 19.4319 8.50049 18.5 8.50049H16.5C15.5681 8.50049 15.1022 8.50049 14.7346 8.34825C14.2446 8.14526 13.8552 7.75591 13.6522 7.26586C13.5 6.89831 13.5 6.43237 13.5 5.50049Z"
|
||||
android:strokeColor="#FF202124"
|
||||
android:strokeWidth="1.5" />
|
||||
</group>
|
||||
</vector>
|
||||
|
|
@ -5,4 +5,8 @@
|
|||
<string name="package_name">social.flotilla</string>
|
||||
<string name="custom_url_scheme">social.flotilla</string>
|
||||
<string name="default_notification_channel_id">flotilla_push</string>
|
||||
<string name="shortcut_messages">Messages</string>
|
||||
<string name="shortcut_search">Search</string>
|
||||
<string name="shortcut_spaces">Spaces</string>
|
||||
<string name="shortcut_inbox">Inbox</string>
|
||||
</resources>
|
||||
|
|
|
|||
47
android/app/src/main/res/xml/shortcuts.xml
Normal file
47
android/app/src/main/res/xml/shortcuts.xml
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<shortcut
|
||||
android:shortcutId="messages"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/shortcut_messages"
|
||||
android:shortcutShortLabel="@string/shortcut_messages">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetPackage="social.flotilla"
|
||||
android:targetClass="social.flotilla.MainActivity"
|
||||
android:data="flotilla://shortcut/messages" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="search"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/shortcut_search"
|
||||
android:shortcutShortLabel="@string/shortcut_search">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetPackage="social.flotilla"
|
||||
android:targetClass="social.flotilla.MainActivity"
|
||||
android:data="flotilla://shortcut/search" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="spaces"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/shortcut_spaces"
|
||||
android:shortcutShortLabel="@string/shortcut_spaces">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetPackage="social.flotilla"
|
||||
android:targetClass="social.flotilla.MainActivity"
|
||||
android:data="flotilla://shortcut/spaces" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:shortcutId="inbox"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/shortcut_inbox"
|
||||
android:shortcutShortLabel="@string/shortcut_inbox">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetPackage="social.flotilla"
|
||||
android:targetClass="social.flotilla.MainActivity"
|
||||
android:data="flotilla://shortcut/inbox" />
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import assert from "node:assert/strict"
|
||||
import {appendFile, cp, readFile, rm, writeFile} from "node:fs/promises"
|
||||
|
||||
const replace = async (file, pattern, replacement) => {
|
||||
const replace = async (file, pattern, replacement, count = 1) => {
|
||||
const source = await readFile(file, "utf8")
|
||||
assert.equal([...source.matchAll(pattern)].length, 1, `Expected one match in ${file}`)
|
||||
assert.equal([...source.matchAll(pattern)].length, count, `Expected ${count} matches in ${file}`)
|
||||
await writeFile(file, source.replace(pattern, replacement))
|
||||
}
|
||||
|
||||
|
|
@ -59,6 +59,12 @@ await replace(
|
|||
/\n {8}<!-- FCM uses[\s\S]*?android:value="@string\/default_notification_channel_id"\n {8}\/>/g,
|
||||
"",
|
||||
)
|
||||
await replace(
|
||||
"android/app/src/main/res/xml/shortcuts.xml",
|
||||
/android:targetPackage="social\.flotilla"/g,
|
||||
'android:targetPackage="social.flotilla.fdroid"',
|
||||
4,
|
||||
)
|
||||
await replace(
|
||||
"src/app.html",
|
||||
/\s*<script\s+defer\s+data-domain="[^"]*"\s+src="https:\/\/plausible\.coracle\.social\/[^"]*"><\/script>/g,
|
||||
|
|
|
|||
|
|
@ -7,10 +7,49 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
if let shortcutItem = launchOptions?[.shortcutItem] as? UIApplicationShortcutItem,
|
||||
let url = shortcutURL(for: shortcutItem.type) {
|
||||
// The proxy retains lastURL for App.getLaunchUrl() before the bridge is ready.
|
||||
_ = ApplicationDelegateProxy.shared.application(application, open: url, options: [:])
|
||||
return false
|
||||
}
|
||||
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
func application(
|
||||
_ application: UIApplication,
|
||||
performActionFor shortcutItem: UIApplicationShortcutItem,
|
||||
completionHandler: @escaping (Bool) -> Void
|
||||
) {
|
||||
guard let url = shortcutURL(for: shortcutItem.type) else {
|
||||
completionHandler(false)
|
||||
return
|
||||
}
|
||||
|
||||
completionHandler(ApplicationDelegateProxy.shared.application(application, open: url, options: [:]))
|
||||
}
|
||||
|
||||
private func shortcutURL(for type: String) -> URL? {
|
||||
let path: String
|
||||
|
||||
switch type {
|
||||
case "social.flotilla.shortcut.messages":
|
||||
path = "messages"
|
||||
case "social.flotilla.shortcut.search":
|
||||
path = "search"
|
||||
case "social.flotilla.shortcut.spaces":
|
||||
path = "spaces"
|
||||
case "social.flotilla.shortcut.inbox":
|
||||
path = "inbox"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
return URL(string: "flotilla://shortcut/\(path)")
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -31,6 +31,41 @@
|
|||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>UIApplicationShortcutItems</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UIApplicationShortcutItemType</key>
|
||||
<string>social.flotilla.shortcut.messages</string>
|
||||
<key>UIApplicationShortcutItemTitle</key>
|
||||
<string>Messages</string>
|
||||
<key>UIApplicationShortcutItemIconSymbolName</key>
|
||||
<string>message</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UIApplicationShortcutItemType</key>
|
||||
<string>social.flotilla.shortcut.search</string>
|
||||
<key>UIApplicationShortcutItemTitle</key>
|
||||
<string>Search</string>
|
||||
<key>UIApplicationShortcutItemIconSymbolName</key>
|
||||
<string>magnifyingglass</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UIApplicationShortcutItemType</key>
|
||||
<string>social.flotilla.shortcut.spaces</string>
|
||||
<key>UIApplicationShortcutItemTitle</key>
|
||||
<string>Spaces</string>
|
||||
<key>UIApplicationShortcutItemIconSymbolName</key>
|
||||
<string>square.grid.2x2</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UIApplicationShortcutItemType</key>
|
||||
<string>social.flotilla.shortcut.inbox</string>
|
||||
<key>UIApplicationShortcutItemTitle</key>
|
||||
<string>Inbox</string>
|
||||
<key>UIApplicationShortcutItemIconSymbolName</key>
|
||||
<string>tray</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@
|
|||
import AppContainer from "@app/components/AppContainer.svelte"
|
||||
import ModalContainer from "@app/components/ModalContainer.svelte"
|
||||
import * as core from "@app/core"
|
||||
import {setupHistory} from "@app/routes"
|
||||
import {goToChat, goToHome, setupHistory} from "@app/routes"
|
||||
import Search from "@app/components/Search.svelte"
|
||||
import {clearModals, getModal, navigate, pushModal} from "@app/modal"
|
||||
import {setupAnalytics} from "@app/analytics"
|
||||
import {setupLogging} from "@app/logger"
|
||||
import "@app/policies"
|
||||
|
|
@ -84,6 +86,33 @@
|
|||
return
|
||||
}
|
||||
|
||||
if (url.protocol === "flotilla:" && url.host === "shortcut") {
|
||||
if (!core.app.get().user) return
|
||||
|
||||
switch (url.pathname) {
|
||||
case "/messages":
|
||||
goToChat()
|
||||
break
|
||||
case "/search": {
|
||||
const modal = getModal()
|
||||
|
||||
if (modal?.component !== Search) {
|
||||
pushModal(Search, {}, {replaceState: Boolean(modal)})
|
||||
}
|
||||
break
|
||||
}
|
||||
case "/spaces":
|
||||
navigate("/spaces")
|
||||
break
|
||||
case "/inbox":
|
||||
clearModals()
|
||||
goToHome()
|
||||
break
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// The iOS share extension can't talk to us directly, so it hands its payload over as a
|
||||
// flotilla://share url
|
||||
if (url.host === "share") {
|
||||
|
|
|
|||
Loading…
Reference in a new issue