Skip to content

Commit 133d75f

Browse files
Switching between "hash" and "path" routing URL strategy (#110)
* Page route URL strategy can be changed * The same behaviour for hash and path strategies * defaultRouteUrlStrategy is hash * Index page notion
1 parent f39312b commit 133d75f

File tree

18 files changed

+127
-32
lines changed

18 files changed

+127
-32
lines changed

client/lib/main.dart

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ import 'package:flutter/foundation.dart';
66
import 'package:flutter/material.dart';
77
import 'package:flutter_redux/flutter_redux.dart';
88
import 'package:redux/redux.dart';
9+
import 'package:url_strategy/url_strategy.dart';
910

11+
import '../utils/platform_utils_non_web.dart'
12+
if (dart.library.js) "../utils/platform_utils_web.dart";
13+
import '../utils/session_store_non_web.dart'
14+
if (dart.library.js) "../utils/session_store_web.dart";
1015
import 'controls/create_control.dart';
1116
import 'models/app_state.dart';
1217
import 'models/page_view_model.dart';
1318
import 'reducers.dart';
14-
import 'session_store/session_store.dart'
15-
if (dart.library.io) "session_store/session_store_io.dart"
16-
if (dart.library.js) "session_store/session_store_js.dart";
1719
import 'web_socket_client.dart';
1820

1921
const bool isProduction = bool.fromEnvironment('dart.vm.product');
@@ -35,6 +37,11 @@ void main([List<String>? args]) async {
3537

3638
if (kIsWeb) {
3739
debugPrint("Flet View is running in Web mode");
40+
var routeUrlStrategy = getRouteUrlStrategy();
41+
debugPrint("URL Strategy: $routeUrlStrategy");
42+
if (routeUrlStrategy == "path") {
43+
setPathUrlStrategy();
44+
}
3845
} else if ((Platform.isWindows || Platform.isMacOS || Platform.isLinux) &&
3946
!kDebugMode) {
4047
debugPrint("Flet View is running in Desktop mode");

client/lib/reducers.dart

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@ import 'package:flet_view/protocol/update_control_props_payload.dart';
77
import 'package:flet_view/web_socket_client.dart';
88
import 'package:flutter/cupertino.dart';
99

10-
import '../utils/platform_utils.dart'
11-
if (dart.library.io) "../utils/platform_utils_io.dart"
12-
if (dart.library.js) "../utils/platform_utils_js.dart";
10+
import '../utils/platform_utils_non_web.dart'
11+
if (dart.library.js) "../utils/platform_utils_web.dart";
12+
import '../utils/session_store_non_web.dart'
13+
if (dart.library.js) "../utils/session_store_web.dart";
1314
import 'actions.dart';
1415
import 'models/app_state.dart';
1516
import 'models/control.dart';
16-
import 'session_store/session_store.dart'
17-
if (dart.library.io) "session_store/session_store_io.dart"
18-
if (dart.library.js) "session_store/session_store_js.dart";
1917
import 'utils/desktop.dart';
2018
import 'utils/uri.dart';
2119

client/lib/session_store/session_store.dart

Lines changed: 0 additions & 9 deletions
This file was deleted.

client/lib/utils/platform_utils.dart

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
bool isProgressiveWebApp() {
22
return false;
33
}
4+
5+
String getRouteUrlStrategy() {
6+
return "";
7+
}

client/lib/utils/platform_utils_js.dart renamed to client/lib/utils/platform_utils_web.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ bool isProgressiveWebApp() {
55
window.matchMedia('(display-mode: fullscreen)').matches ||
66
window.matchMedia('(display-mode: minimal-ui)').matches;
77
}
8+
9+
String getRouteUrlStrategy() {
10+
var meta =
11+
document.head?.querySelector("meta[name='flet-route-url-strategy']");
12+
return meta != null ? meta.attributes["content"]! : "";
13+
}

client/lib/utils/uri.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import 'strings.dart';
22

33
String getWebPageName(Uri uri) {
4-
return trim(uri.path, "/");
4+
var urlPath = trim(uri.path, "/");
5+
if (urlPath != "") {
6+
var pathParts = urlPath.split("/");
7+
if (pathParts.length > 1) {
8+
urlPath = pathParts.sublist(0, 2).join("/");
9+
}
10+
}
11+
return urlPath;
512
}
613

714
String getWebSocketEndpoint(Uri uri) {

client/pubspec.lock

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ packages:
116116
description: flutter
117117
source: sdk
118118
version: "0.0.0"
119+
flutter_web_plugins:
120+
dependency: transitive
121+
description: flutter
122+
source: sdk
123+
version: "0.0.0"
119124
http:
120125
dependency: "direct main"
121126
description:
@@ -137,6 +142,13 @@ packages:
137142
url: "https://pub.dartlang.org"
138143
source: hosted
139144
version: "3.2.0"
145+
js:
146+
dependency: transitive
147+
description:
148+
name: js
149+
url: "https://pub.dartlang.org"
150+
source: hosted
151+
version: "0.6.4"
140152
lints:
141153
dependency: transitive
142154
description:
@@ -247,6 +259,13 @@ packages:
247259
url: "https://pub.dartlang.org"
248260
source: hosted
249261
version: "1.3.0"
262+
url_strategy:
263+
dependency: "direct main"
264+
description:
265+
name: url_strategy
266+
url: "https://pub.dartlang.org"
267+
source: hosted
268+
version: "0.2.0"
250269
vector_math:
251270
dependency: transitive
252271
description:

client/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dependencies:
4040
window_manager: ^0.2.5
4141
http: ^0.13.3
4242
collection: ^1.16.0
43+
url_strategy: ^0.2.0
4344

4445
dev_dependencies:
4546
flutter_test:

client/test/utils/uri_test.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ void main() {
1515
expect(
1616
getWebPageName(Uri.parse('http://localhost:8550/p/test/')), "p/test");
1717
expect(getWebPageName(Uri.parse('http://localhost:8550/p/test')), "p/test");
18+
expect(getWebPageName(Uri.parse('http://localhost:8550/aaa')), "aaa");
19+
expect(getWebPageName(Uri.parse('http://localhost:8550/p/test/store')),
20+
"p/test");
21+
expect(
22+
getWebPageName(
23+
Uri.parse('http://localhost:8550/p/test/store/products/1')),
24+
"p/test");
1825
expect(getWebPageName(Uri.parse('http://localhost:8550/')), "");
1926
expect(getWebPageName(Uri.parse('http://localhost:8550/#/')), "");
2027
});

client/web/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
<!-- Favicon -->
1717
<link rel="icon" type="image/png" href="favicon.png"/>
1818

19+
<!-- Flet specific -->
20+
<meta name="flet-route-url-strategy" content="%FLET_ROUTE_URL_STRATEGY%">
21+
1922
<title>Flet</title>
2023
<link rel="manifest" href="manifest.json">
2124

sdk/python/flet/flet.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def page(
4444
view: AppViewer = WEB_BROWSER,
4545
assets_dir=None,
4646
web_renderer="canvaskit",
47+
route_url_strategy="hash",
4748
):
4849
conn = _connect_internal(
4950
page_name=name,
@@ -52,6 +53,7 @@ def page(
5253
permissions=permissions,
5354
assets_dir=assets_dir,
5455
web_renderer=web_renderer,
56+
route_url_strategy=route_url_strategy,
5557
)
5658
print("Page URL:", conn.page_url)
5759
page = Page(conn, constants.ZERO_SESSION)
@@ -71,6 +73,7 @@ def app(
7173
view: AppViewer = FLET_APP,
7274
assets_dir=None,
7375
web_renderer="canvaskit",
76+
route_url_strategy="hash",
7477
):
7578

7679
if target == None:
@@ -84,6 +87,7 @@ def app(
8487
session_handler=target,
8588
assets_dir=assets_dir,
8689
web_renderer=web_renderer,
90+
route_url_strategy=route_url_strategy,
8791
)
8892
print("App URL:", conn.page_url)
8993

@@ -139,6 +143,7 @@ def _connect_internal(
139143
session_handler=None,
140144
assets_dir=None,
141145
web_renderer=None,
146+
route_url_strategy=None,
142147
):
143148
if share and server == None:
144149
server = constants.HOSTED_SERVICE_URL
@@ -151,7 +156,9 @@ def _connect_internal(
151156
# page with a custom port starts detached process
152157
attached = False if not is_app and port != 0 else True
153158

154-
port = _start_flet_server(port, attached, assets_dir, web_renderer)
159+
port = _start_flet_server(
160+
port, attached, assets_dir, web_renderer, route_url_strategy
161+
)
155162
server = f"http://127.0.0.1:{port}"
156163

157164
connected = threading.Event()
@@ -219,7 +226,7 @@ def _on_ws_failed_connect():
219226
return conn
220227

221228

222-
def _start_flet_server(port, attached, assets_dir, web_renderer):
229+
def _start_flet_server(port, attached, assets_dir, web_renderer, route_url_strategy):
223230

224231
if port == 0:
225232
port = _get_free_tcp_port()
@@ -263,6 +270,10 @@ def _start_flet_server(port, attached, assets_dir, web_renderer):
263270
logging.info(f"Web renderer configured: {web_renderer}")
264271
fletd_env["FLET_WEB_RENDERER"] = web_renderer
265272

273+
if route_url_strategy != None:
274+
logging.info(f"Route URL strategy configured: {route_url_strategy}")
275+
fletd_env["FLET_ROUTE_URL_STRATEGY"] = route_url_strategy
276+
266277
args = [fletd_path, "--port", str(port)]
267278

268279
creationflags = 0

server/config/config.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,10 @@ const (
7171
defaultMasterSecretKey = "master_secret_key"
7272

7373
// development
74-
staticRootDir = "STATIC_ROOT_DIR"
75-
webRenderer = "WEB_RENDERER"
74+
staticRootDir = "STATIC_ROOT_DIR"
75+
webRenderer = "WEB_RENDERER"
76+
routeUrlStrategy = "ROUTE_URL_STRATEGY"
77+
defaultRouteUrlStrategy = "hash"
7678
)
7779

7880
func init() {
@@ -124,6 +126,9 @@ func init() {
124126
// security
125127
viper.SetDefault(cookieSecret, getSecretManagerValue(cookieSecret, defaultCookieSecret))
126128
viper.SetDefault(masterSecretKey, getSecretManagerValue(masterSecretKey, defaultMasterSecretKey))
129+
130+
// development
131+
viper.SetDefault(routeUrlStrategy, defaultRouteUrlStrategy)
127132
}
128133

129134
func getSecretManagerValue(name string, defaultValue string) string {
@@ -288,3 +293,7 @@ func StaticRootDir() string {
288293
func WebRenderer() string {
289294
return viper.GetString(webRenderer)
290295
}
296+
297+
func RouteUrlStrategy() string {
298+
return viper.GetString(routeUrlStrategy)
299+
}

server/model/page_name.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const (
1919
type PageName struct {
2020
Account string
2121
Name string
22+
IsIndex bool
2223
}
2324

2425
func ParsePageName(pageName string) (*PageName, error) {
@@ -27,6 +28,7 @@ func ParsePageName(pageName string) (*PageName, error) {
2728
return &PageName{
2829
Account: publicAccount,
2930
Name: indexPage,
31+
IsIndex: true,
3032
}, nil
3133
}
3234

@@ -62,6 +64,10 @@ func ParsePageName(pageName string) (*PageName, error) {
6264
return nil, fmt.Errorf("page name exceeds the maximum allowed size of %d symbols", maxSlugSize)
6365
}
6466

67+
if p.Account == publicAccount && p.Name == indexPage {
68+
p.IsIndex = true
69+
}
70+
6571
return p, nil
6672
}
6773

server/page/client.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,14 @@ func (c *Client) registerWebClientCore(request *RegisterWebClientRequestPayload)
209209

210210
// get page
211211
page := store.GetPageByName(pageName.String())
212-
if page == nil {
213-
response.Error = pageNotFoundMessage
214-
return
212+
if page == nil && !pageName.IsIndex {
213+
// fallback to index
214+
pageName, _ = model.ParsePageName("")
215+
page = store.GetPageByName(pageName.String())
216+
if page == nil {
217+
response.Error = pageNotFoundMessage
218+
return
219+
}
215220
}
216221

217222
// func: check if "Sign in required" response should be sent

server/server/server.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,40 @@ func Start(ctx context.Context, wg *sync.WaitGroup, serverPort int) {
9999
router.NoRoute(func(c *gin.Context) {
100100

101101
if !strings.HasPrefix(c.Request.RequestURI, apiRoutePrefix+"/") {
102-
urlPath := strings.TrimRight(c.Request.URL.Path, "/") + "/"
103-
log.Debugln("Request path:", urlPath)
102+
baseHref := strings.Trim(c.Request.URL.Path, "/")
103+
log.Debugln("Request path:", baseHref)
104+
105+
if baseHref != "" {
106+
hrefParts := strings.Split(baseHref, "/")
107+
if len(hrefParts) > 1 {
108+
baseHref = strings.Join(hrefParts[:2], "/")
109+
if store.GetPageByName(baseHref) == nil {
110+
// fallback to index page
111+
baseHref = ""
112+
}
113+
} else {
114+
baseHref = ""
115+
}
116+
}
117+
118+
if baseHref != "" {
119+
baseHref = "/" + baseHref + "/"
120+
} else {
121+
baseHref = "/"
122+
}
104123

105124
index, _ := assetsFS.Open(siteDefaultDocument)
106125
indexData, _ := ioutil.ReadAll(index)
107126

108127
// base path
109128
indexData = bytes.Replace(indexData,
110129
[]byte("<base href=\"/\">"),
111-
[]byte("<base href=\""+urlPath+"\">"), 1)
130+
[]byte("<base href=\""+baseHref+"\">"), 1)
131+
132+
// route URL strategy
133+
indexData = bytes.Replace(indexData,
134+
[]byte("%FLET_ROUTE_URL_STRATEGY%"),
135+
[]byte(config.RouteUrlStrategy()), 1)
112136

113137
// web renderer
114138
if config.WebRenderer() != "" {

0 commit comments

Comments
 (0)