diff --git a/src/main.py b/src/main.py
index e6785f6..7afaade 100644
--- a/src/main.py
+++ b/src/main.py
@@ -29,6 +29,8 @@ gi.require_version('Handy', '1')
from gi.repository import Handy
Handy.init()
+from .widgets import LoginPage, Timeline, PostItem
+
class Application(Gtk.Application):
def __init__(self):
@@ -127,11 +129,13 @@ class Application(Gtk.Application):
global_tl = Timeline('global')
self.stack.add_titled(global_tl, "global", "Global")
+ new_post_button = Gtk.Button.new_from_icon_name('list-add-symbolic', 1)
+ self.header.pack_start(new_post_button)
+
reload_button = Gtk.Button.new_from_icon_name('view-refresh-symbolic', 1)
reload_button.connect('clicked', self.emit_refresh)
self.header.pack_start(reload_button)
- new_post_button = Gtk.Button.new_from_icon_name('list-add-symbolic', 1)
- self.header.pack_start(new_post_button)
+
self.header.show_all()
self.stack.show_all()
@@ -140,155 +144,6 @@ class Application(Gtk.Application):
timeline = self.stack.get_visible_child()
timeline.emit('refresh')
-class LoginPage(Gtk.Box):
-
- __gsignals__ = {
- 'login': (GObject.SIGNAL_RUN_FIRST, None, (str,))
- }
-
- def __init__(self):
- super().__init__(orientation='vertical')
- self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
-
- client_id = "1PiUzxfX_CQxKvtz93lUzPX9-FMtz-va"
- redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
- scope = "basic,stream,write_post,follow,presence,messages,files,polls"
-
- uri = "https://pnut.io/oauth/authenticate"
- uri += "?client_id=" + client_id
- uri += "&redirect_uri=" + redirect_uri
- uri += "&scope=" + scope
- uri += "&response_type=token"
-
- self.login_button = Gtk.LinkButton.new_with_label(uri, "Log In to pnut.io")
- self.login_button.connect("clicked", self.prompt_code)
-
- self.set_center_widget(self.login_button)
-
- def prompt_code(self, button):
- self.remove(self.login_button)
-
- label = Gtk.Label()
- label.set_markup('Enter authorization code')
- self.code = Gtk.Entry()
-
- paste_button = Gtk.Button(label="Paste from clipboard")
- paste_button.connect("clicked", self.paste_code)
-
- cancel_button = Gtk.Button(label="Cancel")
- cancel_button.connect("clicked", self.cancel_login)
- confirm_button = Gtk.Button(label="Confirm")
- confirm_button.connect("clicked", self.confirm_login)
- lbox = Gtk.Box(orientation='horizontal')
- lbox.pack_start(cancel_button, True, True, 0)
- lbox.pack_start(confirm_button, True, True, 0)
-
- vbox = Gtk.Box(orientation='vertical')
- vbox.pack_start(label, False, False, 10)
- vbox.pack_start(self.code, False, False, 10)
- vbox.pack_start(paste_button, False, False, 10)
- vbox.add(lbox)
-
- hbox = Gtk.Box(orientation='horizontal')
- hbox.set_center_widget(vbox)
-
- self.set_center_widget(hbox)
- self.show_all()
-
- def paste_code(self, button):
- text = self.clipboard.wait_for_text()
- if text is not None:
- self.code.set_text(text)
-
- def cancel_login(self, button):
- # TODO: something actually useful here
- logging.debug("uh cancel i guess")
-
- def confirm_login(self, button):
- code = self.code.get_text()
- self.emit('login', code)
-
-class Timeline(Gtk.Box):
-
- __gsignals__ = {
- 'refresh': (GObject.SIGNAL_RUN_FIRST, None, ())
- }
-
- def __init__(self, stream):
- super().__init__(orientation='vertical')
-
- scroller = Gtk.ScrolledWindow(
- halign='fill',
- kinetic_scrolling=True
- )
- self.view = Gtk.ListBox(
- selection_mode=Gtk.SelectionMode.NONE
- )
- scroller.add(self.view)
- self.pack_start(scroller, True, True, 0)
-
- self.stream = stream
- self.load_timeline()
-
- def load_timeline(self):
- if self.stream == 'unified':
- posts, meta = pnutpy.api.users_post_streams_unified()
- elif self.stream == 'mentions':
- posts, meta = pnutpy.api.users_mentioned_posts('me')
- elif self.stream == 'bookmarks':
- posts, meta = pnutpy.api.users_bookmarked_posts('me')
- else:
- posts, meta = pnutpy.api.posts_streams_global()
-
- for item in posts:
- if 'is_deleted' in item:
- continue
- self.view.add(PostItem(item))
-
- def do_refresh(self):
- rows = self.view.get_children()
- for item in rows:
- self.view.remove(item)
- self.load_timeline()
- self.show_all()
-
-class PostItem(Gtk.ListBoxRow):
-
- def __init__(self, post):
- super(Gtk.ListBoxRow, self).__init__()
- self.post = post
-
- self.box = Gtk.Box(orientation='vertical')
- self.add(self.box)
-
- # name container
- self.name_box = Gtk.Box(orientation='vertical')
- self.username = Gtk.Label(label="@" + post.user.username, xalign=0)
- self.name = Gtk.Label(xalign=0)
- if 'name' in post.user:
- self.name.set_markup(f"{post.user.name}")
- self.name_box.pack_start(self.name, True, True, 0)
- self.name_box.pack_start(self.username, True, True, 0)
-
- # header container
- self.h_box = Gtk.Box(orientation='horizontal')
- self.avatar = Handy.Avatar(size=32)
- # TODO: get the actual image
- self.h_box.pack_start(self.avatar, False, False, 18)
- self.h_box.pack_start(self.name_box, False, False, 0)
-
- # content container
- self.c_box = Gtk.Box(orientation='horizontal')
- self.content = Gtk.Label(wrap=True, xalign=0)
- # TODO: parse content links
- if 'content' in post:
- self.content.set_text(post.content.text)
- # TODO: add media
- self.c_box.pack_start(self.content, True, True, 18)
-
- self.box.pack_start(self.h_box, True, True, 10)
- self.box.pack_start(self.c_box, True, True, 10)
-
def main(version):
logging.basicConfig(level=logging.DEBUG)
app = Application()
diff --git a/src/meson.build b/src/meson.build
index 984592f..860f7f5 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -28,6 +28,7 @@ configure_file(
squeak_sources = [
'__init__.py',
'main.py',
+ 'widgets.py',
]
install_data(squeak_sources, install_dir: moduledir)
diff --git a/src/widgets.py b/src/widgets.py
new file mode 100644
index 0000000..767c8f7
--- /dev/null
+++ b/src/widgets.py
@@ -0,0 +1,179 @@
+# widgets.py
+#
+# Copyright 2020 Morgan McMillian
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import sys
+import gi
+import os
+import pnutpy
+import logging
+
+gi.require_version('Gdk', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import GObject, Gdk, Gtk, Gio, GLib
+
+gi.require_version('Handy', '1')
+from gi.repository import Handy
+
+class LoginPage(Gtk.Box):
+
+ __gsignals__ = {
+ 'login': (GObject.SIGNAL_RUN_FIRST, None, (str,))
+ }
+
+ def __init__(self):
+ super().__init__(orientation='vertical')
+ self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
+
+ client_id = "1PiUzxfX_CQxKvtz93lUzPX9-FMtz-va"
+ redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
+ scope = "basic,stream,write_post,follow,presence,messages,files,polls"
+
+ uri = "https://pnut.io/oauth/authenticate"
+ uri += "?client_id=" + client_id
+ uri += "&redirect_uri=" + redirect_uri
+ uri += "&scope=" + scope
+ uri += "&response_type=token"
+
+ self.login_button = Gtk.LinkButton.new_with_label(uri, "Log In to pnut.io")
+ self.login_button.connect("clicked", self.prompt_code)
+
+ self.set_center_widget(self.login_button)
+
+ def prompt_code(self, button):
+ self.remove(self.login_button)
+
+ label = Gtk.Label()
+ label.set_markup('Enter authorization code')
+ self.code = Gtk.Entry()
+
+ paste_button = Gtk.Button(label="Paste from clipboard")
+ paste_button.connect("clicked", self.paste_code)
+
+ cancel_button = Gtk.Button(label="Cancel")
+ cancel_button.connect("clicked", self.cancel_login)
+ confirm_button = Gtk.Button(label="Confirm")
+ confirm_button.connect("clicked", self.confirm_login)
+ lbox = Gtk.Box(orientation='horizontal')
+ lbox.pack_start(cancel_button, True, True, 0)
+ lbox.pack_start(confirm_button, True, True, 0)
+
+ vbox = Gtk.Box(orientation='vertical')
+ vbox.pack_start(label, False, False, 10)
+ vbox.pack_start(self.code, False, False, 10)
+ vbox.pack_start(paste_button, False, False, 10)
+ vbox.add(lbox)
+
+ hbox = Gtk.Box(orientation='horizontal')
+ hbox.set_center_widget(vbox)
+
+ self.set_center_widget(hbox)
+ self.show_all()
+
+ def paste_code(self, button):
+ text = self.clipboard.wait_for_text()
+ if text is not None:
+ self.code.set_text(text)
+
+ def cancel_login(self, button):
+ # TODO: something actually useful here
+ logging.debug("uh cancel i guess")
+
+ def confirm_login(self, button):
+ code = self.code.get_text()
+ self.emit('login', code)
+
+class Timeline(Gtk.Box):
+
+ __gsignals__ = {
+ 'refresh': (GObject.SIGNAL_RUN_FIRST, None, ())
+ }
+
+ def __init__(self, stream):
+ super().__init__(orientation='vertical')
+
+ scroller = Gtk.ScrolledWindow(
+ halign='fill',
+ kinetic_scrolling=True
+ )
+ self.view = Gtk.ListBox(
+ selection_mode=Gtk.SelectionMode.NONE
+ )
+ scroller.add(self.view)
+ self.pack_start(scroller, True, True, 0)
+
+ self.stream = stream
+ self.load_timeline()
+
+ def load_timeline(self):
+ if self.stream == 'unified':
+ posts, meta = pnutpy.api.users_post_streams_unified()
+ elif self.stream == 'mentions':
+ posts, meta = pnutpy.api.users_mentioned_posts('me')
+ elif self.stream == 'bookmarks':
+ posts, meta = pnutpy.api.users_bookmarked_posts('me')
+ else:
+ posts, meta = pnutpy.api.posts_streams_global()
+
+ for item in posts:
+ if 'is_deleted' in item:
+ continue
+ self.view.add(PostItem(item))
+
+ def do_refresh(self):
+ rows = self.view.get_children()
+ for item in rows:
+ self.view.remove(item)
+ self.load_timeline()
+ self.show_all()
+
+class PostItem(Gtk.ListBoxRow):
+
+ def __init__(self, post):
+ super(Gtk.ListBoxRow, self).__init__()
+ self.post = post
+
+ self.box = Gtk.Box(orientation='vertical')
+ self.add(self.box)
+
+ # name container
+ self.name_box = Gtk.Box(orientation='vertical')
+ self.username = Gtk.Label(label="@" + post.user.username, xalign=0)
+ self.name = Gtk.Label(xalign=0)
+ if 'name' in post.user:
+ self.name.set_markup(f"{post.user.name}")
+ self.name_box.pack_start(self.name, True, True, 0)
+ self.name_box.pack_start(self.username, True, True, 0)
+
+ # header container
+ self.h_box = Gtk.Box(orientation='horizontal')
+ self.avatar = Handy.Avatar(size=32)
+ # TODO: get the actual image
+ self.h_box.pack_start(self.avatar, False, False, 18)
+ self.h_box.pack_start(self.name_box, False, False, 0)
+
+ # content container
+ self.c_box = Gtk.Box(orientation='horizontal')
+ self.content = Gtk.Label(wrap=True, xalign=0)
+ # TODO: parse content links
+ if 'content' in post:
+ self.content.set_text(post.content.text)
+ # TODO: add media
+ self.c_box.pack_start(self.content, True, True, 18)
+
+ self.box.pack_start(self.h_box, True, True, 10)
+ self.box.pack_start(self.c_box, True, True, 10)
+